@Configuration
@EnableJdbcRepositories
class ApplicationConfig extends AbstractJdbcConfiguration {
@Bean
public DataSource dataSource() {
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
return builder.setType(EmbeddedDatabaseType.HSQL).build();
}
@Bean
NamedParameterJdbcOperations namedParameterJdbcOperations(DataSource dataSource) {
return new NamedParameterJdbcTemplate(dataSource);
}
@Bean
TransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
@EnableJdbcRepositories
: Repository 인터페이스 구현체 생성AbstractJdbcConfiguration
: Spring Data JDBC가 요구하는 default bean 제공NamedParameterJdbcOperations
생성, transaction 관리작동 원리
빌드한 데이터 소스를 이용하여 NamedParameterJdbcOperations와 TransactionManager를 셋업하고 @EnableJdbcRepositories
를 통해 스프링 데이터 JDBC 레파지토리를 활성화한다. (base package를 지정하지 않으면 configuration 클래스가 속한 패키지 사용)
But, 스프링 부트를 사용하면 spring-boot-starter-data-jdbc
의존성을 build.gradle에 추가해주고 application properties에 data source만 configure해주면 된다😆
# build.gradle
implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
# application.properties
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/(데이터베이스 이름)?characterEncoding=UTF-8
spring.datasource.username=(사용자 이름)
spring.datasource.password=(사용자 비밀번호)
CrudRepository.save(…)
메서드로 aggregate root에 새로운 entity 삽입 가능리플렉션의 오버헤드를 줄이기 위해 Spring Data는 런타임 시 기본으로 생성되는 팩터리 클래스를 사용한다.
class Person {
Person(String firstname, String lastname) { … }
}
class PersonObjectInstantiator implements ObjectInstantiator {
Object newInstance(Object... args) {
return new Person((String) args[0], (String) args[1]);
}
}
이러한 방식으로 객체를 생성하면 reflection에 비해 10% 정도 성능을 개선할 수 있는데, private, non-static inner, CGLib proxy 클래스를 사용하거나 Spring Data에 의해 사용될 생성자가 private인 경우 reflection을 이용하여 entity가 초기화된다.
생성자
- no-argument constructor를 제일 우선으로 사용한다.
- argument를 받는 생성자 한 개만 있다면 그 생성자를 사용한다.
- argument를 받는 생성자가 여러 개 있다면
@PersistenceConstructor
애노테이션이 붙은 생성자를 사용한다.
권장 사항
- 불변 객체를 사용해라
- all-args constructor를 제공해라
- 생성자 오버로딩보다는 팩터리 메서드를 사용해라(
@PersistenceConstructor
를 사용하지 않아도 됨)- 롬복을 사용해라
도메인 객체의 인스턴스를 생성하고 저장을 위한 자료구조를 맵핑하는 것
NamingStrategy.getReverseColumnName(PersistentPropertyPathExtension path)
를 구현하면 된다.Map<simple type, some entity>
이나 List<some entity>
의 경우 foreign key로 사용할 컬럼 외에도 참조하는 테이블 이름 뒤에 _key
를 붙인 컬럼이 필요하다. @MappedCollection(idColumn="your_column_name", keyColumn="your_key_column_name")
을 통해 이름을 설정할 수 있다.자바 데이터 모델 안에 value object를 갖기 위해 사용
public class MyEntity {
@Id
Integer id;
@Embedded(onEmpty = USE_NULL)
EmbeddedEntity embeddedEntity;
}
public class EmbeddedEntity {
String name;
}
@Embedded(onEmpty = USE_NULL)
이기 때문에 name 컬럼이 null이 되면 embeddedEntity
전체 속성이 null로 설정된다. @Embedded.Nullable
로 써도 된다.USE_EMPTY
로 설정한다면 기본 생성자 또는 nullable parameter 값을 받는 생성자로 새로운 인스턴스가 생성된다. AbstractJdbcConfiguration
를 상속하고 jdbcCustomConversions()
를 오버라이드하면 커스텀 컨버터를 등록할 수 있다.
@Configuration
public class DataJdbcConfiguration extends AbstractJdbcConfiguration {
@Override
public JdbcCustomConversions jdbcCustomConversions() {
return new JdbcCustomConversions(Collections.singletonList(TimestampTzToDateConverter.INSTANCE));
}
@ReadingConverter
enum TimestampTzToDateConverter implements Converter<TIMESTAMPTZ, Date> {
INSTANCE;
@Override
public Date convert(TIMESTAMPTZ source) {
//...
}
}
}
@ReadingConverter
어노테이션을 붙이면 데이터베이스로부터 데이터를 읽어올 때만 컨버터가 적용되고, @WritingConverter
를 붙이면 데이터베이스에 데이터를 입력할 때 컨버터가 적용된다.
@Table
애노테이션을 달아 데이터베이스에 있는 테이블과 엔티티 클래스를 맵핑해줄 수 있다.
@Table("CUSTOM_TABLE_NAME")
public class MyEntity {
@Id
Integer id;
String name;
}
기본 NamingStrategy가 데이터베이스에 있는 컬럼명과 일치하지 않는 경우, @Column
애노테이션을 통해 컬럼 이름을 바꿀 수 있다.
public class MyEntity {
@Id
Integer id;
@Column("CUSTOM_COLUMN_NAME")
String name;
}
@Query
와 함께 원하는 쿼리를 작성해주면 된다.public interface UserRepository extends CrudRepository<User, Long> {
@Query("select firstName, lastName from User u where u.emailAddress = :email")
User findByEmailAddress(@Param("email") String email);
}
Source
Jane 최고!!!!👍 학습하는데 큰 도움이 되었습니다!🙇♀️