Web

1. SpringBoot - JPA server using MySQL

kakaroo 2022. 2. 18. 12:33
반응형

article logo

 

안드로이드에서 정해진 시간마다 데이터베이스에 시간/위치 정보를 저장할 수 있게

시간 정보와 Location 정보의 데이터베이스를 처리하는 서버를 만들겠습니다.

 

먼저 Project를 생성한 뒤, dependency를 추가해 줍니다.

<pom.xml>

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.6.3</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.kakaroo.springlocationjpa</groupId>
	<artifactId>SpringLocationJPA</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>SpringLocationJPA</name>
	<description>Eclipse JPA project for Spring Boot</description>
	<properties>
		<java.version>11</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>2.2.2</version>
		</dependency>
		
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>

		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<configuration>
					<excludes>
						<exclude>
							<groupId>org.projectlombok</groupId>
							<artifactId>lombok</artifactId>
						</exclude>
					</excludes>
				</configuration>
			</plugin>
		</plugins>
	</build>

</project>

 

db 이름과 user 정보를 저장해 줍니다.

<application.properties>

spring.datasource.url=jdbc:mysql://localhost:3306/locationdb?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Seoul
spring.datasource.username=kakaroo
spring.datasource.password=kakaroo11
logging.level.com.kakaroo.springlocationjpa.LocationRespository=TRACE


<MySQL database 생성>

데이터베이스 생성

sql>create database locationdb default CHARACTER SET UTF8

 

생성한 데이터베이스로 이동

sql>use locationdb

 

계정생성

sql>create user 'kakaroo@localhost' identified by 'kakaroo11'

 

권한 부여

sql>grant all privileges on *.* to 'kakaroo'@'localhost'

sql>flush privileges

 


Table Entity 는 id, 시간정보, 위도, 경도로 정의합니다.

@Getter
@NoArgsConstructor
@Entity
public class LocationEntity {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;
	private LocalDateTime time;
	private Double latitude;
	private Double longitude;
	
	@Builder
	public LocationEntity(Long id, LocalDateTime time, Double latitude, Double longitude) {
		this.id = id;
		this.time = time;
		this.latitude = latitude;
		this.longitude = longitude;
	}
}

Create, ReadAll, Delete, DeleteAll 을 구현해 줍니다.

(Update와 ReadById는 사용하지 않기 때문에 제외합니다.)

@RequiredArgsConstructor
@Service
public class LocationService {
	
	private final LocationRepository repository;
	
	@Transactional
	public Long create(LocationCreateDto createDto) {
		return repository.save(createDto.toEntity()).getId();
	}
	
	@Transactional(readOnly = true)
	public List<LocationReadDto> readAll() {
		List<LocationReadDto> list = repository.findAll().stream().map(entity -> new LocationReadDto(entity)).collect(Collectors.toList());
		return list;		
	}
	
	@Transactional
	public Long delete(Long id) {
		LocationEntity entity = repository.findById(id).orElseThrow(() -> new IllegalArgumentException("delete error!!! id: " + id));
		repository.delete(entity);
		return id;
	}
	
	@Transactional
	public Long deleteAll() {		
		repository.deleteAll();
		return repository.count();
	}
}

 

* 구현부분에 대해 궁금하신 분은 이전에 포스팅했던 글을 참조하시면 됩니다.

https://kakaroo.tistory.com/49

 

Spring Boot - Eclipse Maven으로 Spring Web Layer 실습

아래 기등록한 포스트는 IntelliJ + Gradle 로 구현하였기에 Eclipse + Maven 환경으로 다시 해 보겠습니다. (공부삼아.. ) https://kakaroo.tistory.com/39 2 - Spring Boot - JPA 구현 by Spring Web Layer Spri..

kakaroo.tistory.com


Error : SQL table doesn't exist 

실행시켰더니 테이블이 생성되어 있지 않아 에러가 발생합니다.

java.sql.SQLSyntaxErrorException: Table 'locationdb.location_entity' doesn't exist
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:120) ~[mysql-connector-java-8.0.28.jar:8.0.28]
at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122) ~[mysql-connector-java-8.0.28.jar:8.0.28]
at com.mysql.cj.jdbc.ClientPreparedStatement.executeInternal(ClientPreparedStatement.java:953) ~[mysql-connector-java-8.0.28.jar:8.0.28]

 

<application.properties> 속성 추가

spring.jpa.hibernate.ddl-auto=create

spring.jpa.hibernate.ddl-auto 속성
create: 기존 테이블을 삭제하고 새로 생성 (DROP - CREATE)
create-drop: create와 동일하나 어플리케이션 종료시 테이블 삭제 (DROP - CREATE - DROP)
update: 데이터베이스 테이블 - 엔터티 매핑정보를 비교해서 변경사항만 수정
validate: 데이터베이스 테이블 - 엔터티 매핑정보를 비교해서 차이가있으면 어플리케이션을 실행하지 않음
none: 자동 생성 기능을 사용하지 않음

위 auto create를 실행한 뒤, Default item 을 넣어서 table에 값이 잘 들어가는지 확인해 보겠습니다.

SQL에 시간 정보는 아래와 같이 LocalDateTime 타입으로 값을 변환해서 저장합니다.

@GetMapping("/")
public String init() {
    Double latitude = 37.3863871;
    Double longitude = 126.9648526;
    for (int i = 0; i < 5; i++) {

        //java.sql.TimeStamp
        SimpleDateFormat formatter = new SimpleDateFormat ("yyyy-MM-dd hh:mm:ss");
        Calendar cal = Calendar.getInstance();
        String today = formatter.format(cal.getTime());

        Timestamp ts = Timestamp.valueOf(today);
        System.out.println("timestamp : "+ts);		

        LocalDateTime ldt = ts.toLocalDateTime();
        System.out.println("SQL type of timestamp : "+ldt);	

        Double reviseValueA = i * 0.001;
        Double reviseValueB = i * 0.002;			
        repository
                .save(LocationEntity.builder().time(ldt).latitude(latitude+reviseValueA).longitude(longitude+reviseValueB).build());
    }
    return "Default item is inserted";
}

 

다행히 잘 들어갔네요.

default imte이 table에 잘 생성됨
default imte이 table에 잘 생성됨

 

 

코드는 아래에 있습니다.

https://github.com/kakarooJ/JPA-MySql-Location-Server

 

GitHub - kakarooJ/JPA-MySql-Location-Server

Contribute to kakarooJ/JPA-MySql-Location-Server development by creating an account on GitHub.

github.com

 

 

서버측은 간단하게 구성이 끝났습니다.

다음 포스팅에서는 클라이언트가 되는 안드로이드에서 값을 주기적으로 저장하고,

저장된 정보를 읽어와서 지도화면에 시간별로 출력해 보겠습니다.

 


 

 

Auto_Increment 속성이 있는 ID가 table value가 추가될 때마다 증가해서

delete all 이후, id를 1부터 reset 하기 위한 query가 필요해졌습니다.

 

Controller 에 EntityManager의 createNativeQuery를 적용하여 query를 구현할 수 있습니다.

//LocationService.java
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

public class LocationService {
	@PersistenceContext
	EntityManager entityManager;
    ..

	@Transactional
	public Long deleteAll() {		
		repository.deleteAll();
		Long count = repository.count();
		
		String jpql = "ALTER TABLE locations AUTO_INCREMENT = 1";	//locations : table name
		entityManager.createNativeQuery(jpql).executeUpdate();
				
		return count;
	}
}

이전에 Class 이름(LocationEntity)으로 생성된 테이블 이름이 location_entity로 너무 길고 맘에 들지 않아,

아래와 같이 테이블 이름을 locations 로 변경했기 때문에 위 query 문의 테이블 이름이 변경되었습니다.

@Entity

@Table(name = "locations")
public class LocationEntity {

 

delete all 이후, add 했을 때 아래와 같이 id가 1부터 새로 부여됩니다.

 

 

 

database는 서버가 재실행될때마다 drop 되어서 create 값에서 update로 변경했습니다.

spring.jpa.hibernate.ddl-auto=update
반응형