[Spring Boot/JPA] 스프링 부트에서 JPA시작하기
Spring Boot + Gradle 환경에서 JPA를 시작하는 방법을 안내합니다. 프로젝트 설정, EntityManager 사용, 엔티티 설계까지 다룹니다.

JPA 공부를 위해 자바 ORM 표준 JPA 프로그래밍 교재를 구입했다.
교재는 시작을 Spring, maven 기반에서 하지만 Spring Boot, gradle 기반으로 JPA를 사용하기 위해서 약간의 설정을 바꾸어 주었다.
프로젝트 세팅은 Spring Initializr를 사용한다.

IntelliJ에서 기본으로 제공해주는 Spring Initializr를 사용했지만 https://start.spring.io 에서 만들어도 된다.]
build.gradle에 필요한 의존성들을 넣어준다.
mysql을 사용하기 때문에 mysql-connector를 추가해주었고 자신엌게 맞는 connector를 추가해주면 된다.
나머지 세개의 의존성은 그대로 넣는 것을 추천한다.

Postman으로 API를 테스트할 수 있지만 개인적으로 Swagger를 선호하는 편이기 때문에 Swagger의존성도 추가해주었다.

jpa database를 만들어 준 후 application.properties에 db정보를 입력해준다.
server.port= 8080
server.servlet.encoding.charset=UTF-8
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true
spring.datasource.driver-class-name= com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/jpa?useSSL=false&useUnicode=true&serverTimezone=Asia/Seoul
spring.datasource.username=[username]
spring.datasource.password=[password]추가로 jpa관련 설정윀 입력해준다.
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.mvc.pathmatch.matching-strategy=ant_path_matcher
스웨거를 사용한다면 스프링부트와 스웨거의 버전이 맞지 않는 에러를 무시하기 위해서 추가로 설정을 해준다.
spring.mvc.pathmatch.matching-strategy=ant_path_matcher목표하는 패키지 구조는 다음과 같다. mapper를 만들어 사용한다면 이후 mapper도 추가될 예정이다.
EntityManager, EntityManagerFactory로 CRUD를 테스트하는 수준에서는 mapper가 굳이 필요하지 않기 때문에 추가하지 않았다.

config 패키지는 swagger 설정을 위해 사용했기 때문에 swagger를 사용하지 않는다면 만들지 않아도 된다.
컨트롤러, 서비스는 다음과 같이 구성되어있다.
@RestController
@RequestMapping("/user")
public class UserController {
@Resource(name = "userServiceImpl")
UserService userService;
@PostMapping("/")
public ResponseEntity createUser(@RequestBody User user){
userService.createUser(user);
return new ResponseEntity("ok", HttpStatus.CREATED);
}
}public interface UserService {
void createUser(User user);
}@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {
@PersistenceContext
EntityManager em;
@Override
@Transactional
public void createUser(User user) {
UserEntity userEntity = new UserEntity();
userEntity.setName(user.getName());
userEntity.setAge(user.getAge());
em.persist(userEntity);
userEntity.setAge(23);
}
}Spring에서는 EntityManager, EntityManagerFactory에 대해 설정파일(persistence.xml)이 있다.
SpringBoot에서는 자동으로 설정해주기 때문에 등록되어있는 Manager, ManagerFatory를 가져와서 사용해주면 된다.
@PersistenceContext
EntityManager em;
@PersistenceUnit
EntityManagerFactory emf;entitiy와 domain객체는 각각 BaseEntity, BaseObject를 가지고 있다. 기본적으로 db에서 id, created_at, updated_at, is_deleted를 사용할 것이기 때문에 공통으로 사용하는 것들을 묶어준 entity, domain객체이다.
Lombok을 사용해서 getter, setter를 설정해주었다.
@Getter
@Setter
@JsonInclude(JsonInclude.Include.NON_NULL)
public abstract class BaseObject {
@ApiModelProperty(hidden = true)
protected Long id;
@ApiModelProperty(hidden = true)
@JsonProperty("isDeleted")
protected boolean isDeleted;
@ApiModelProperty(hidden = true)
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Seoul")
protected Timestamp createdAt;
@ApiModelProperty(hidden = true)
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Seoul")
protected Timestamp updatedAt;
}@Getter
@Setter
@MappedSuperclass
public abstract class BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
protected Long id;
@Column(name="is_deleted")
protected Boolean isDeleted;
@CreationTimestamp
@Column(name = "created_at",updatable = false)
protected Timestamp createdAt;
@UpdateTimestamp
@Column(name = "updated_at",updatable = false)
protected Timestamp updatedAt;
public BaseEntity() {
this.isDeleted = false;
}
}이후 UserEntity와 User 도메인 객체는 위의 객체를 상속받아서 만든다.
@Getter
@Setter
@JsonInclude(JsonInclude.Include.NON_NULL)
public class User extends BaseObject {
private String name;
private Integer age;
}@Entity
@Table(name="user")
@SQLDelete(sql = "UPDATE user SET is_deleted = true WHERE id=?")
@Where(clause = "is_deleted = false")
@Getter
@Setter
@NoArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL)
public class UserEntity extends BaseEntity {
@Column(name = "name")
private String name;
@Column(name = "age")
private Integer age;
}기본적으로 Entity는 DB에서 하드딜리트가 되기 때문에 하드딜리트를 하지 않고 is_deleted 컬럼을 사용하기 위해서 @SQLDelete, @Where어노테이션으로 설정해주었다.
이후 스웨거로 테스트해보면 값이 올바르게 들어와 있는 것을 볼 수 있다.



