-
Notifications
You must be signed in to change notification settings - Fork 34
User Guide (KR)
Spring Data JDBC Reference Documentation
-
Spring Boot Starter Data JDBC Plus SQL
- 1.1. Gradle / Maven Dependency
- 1.2. JdbcRepositorySupport, JdbcDaoSupport
- 1.3. JdbcReactiveDaoSupport
- 1.4. EntityRowMapper, AggregateResultSetExtractor
- 1.4.1. EntityRowMapper
- 1.4.2. AggregateResultSetExtractor
- 1.5. SqlGeneratorSupport (SqlAware)
- 1.6. SqlParameterSource
- 1.7. SingleValueSelectTrait
- 1.8. SqlParameterSourceFactory
- 1.9. SqlTableAlias
- 1.10. @SqlFunction
-
Spring Boot Starter Data JDBC Plus Repository
- 2.1. JdbcRepository
- 2.2. Reactive Type Support
- 2.3. @SoftDeleteColumn
Spring Data JDBC λ₯Ό μ¬μ©νλ©΄μ, μ§μ SQL μ μμ±ν λ λμμ΄ λλ κΈ°λ₯λ€μ μ 곡ν©λλ€.
- Gradle
dependencies {
implementation("com.navercorp.spring:spring-boot-starter-data-jdbc-plus-sql:3.3.5")
}- Maven
<dependency>
<groupId>com.navercorp.spring</groupId>
<artifactId>spring-boot-starter-data-jdbc-plus-sql</artifactId>
<version>3.3.5</version>
</dependency>- Java Codes
@Table("n_order")
@Data
public class Order {
@Id
@Column("order_no")
private Long orderNo;
@Column("price")
private long price;
@Column("purchaser_no")
private String purchaserNo;
}
public interface OrderRepository extends CrudRepository<Order, Long>, OrderRepositoryCustom {
}
public interface OrderRepositoryCustom {
List<Order> findByPurchaserNo(String purchaserNo);
}
public class OrderRepositoryImpl extends JdbcRepositorySupport<Order> implements OrderRepositoryCustom {
private final OrderSql sqls;
public OrderRepositoryImpl(EntityJdbcProvider entityJdbcProvider) {
super(Order.class, entityJdbcProvider);
this.sql = super.sqls(OrderSql::new);
}
@Override
public List<Order> findByPurchaserNo(String purchaserNo) {
String sql = this.sql.selectByPurchaserNo();
return find(sql, mapParameterSource()
.addValue("purchaserNo", purchaserNo));
}
}- Groovy codes for SQL
implementation("org.codehaus.groovy:groovy:${groovyVersion}")class OrderSql extends SqlGeneratorSupport {
String selectByPurchaserNo() {
"""
SELECT ${sql.columns(Order)}
FROM n_order
WHERE purchaser_no = :purchaserNo
"""
}
}- Customizing Individual Repositories λ‘ SQL μ€ν μ½λλ₯Ό μ§μ μμ±ν λ νμν μ½λ λ° μ§μ λ©μλλ₯Ό μ 곡νλ€.
- λ΄λΆμ μΌλ‘
JdbcOperations(NamedParameterJdbcTemplate)μ μ¬μ©ν©λλ€.
# JdbcRepositorySupport
public class OrderRepositoryImpl extends JdbcRepositorySupport<Order> implements OrderRepositoryCustom {
private final OrderSql sqls;
public OrderRepositoryImpl(EntityJdbcProvider entityJdbcProvider) {
super(Order.class, entityJdbcProvider);
this.sql = super.sqls(OrderSql::new);
}
@Override
public List<Order> findByPurchaserNo(String purchaserNo) {
String sql = this.sql.selectByPurchaserNo();
return find(sql, mapParameterSource()
.addValue("purchaserNo", purchaserNo));
}
}
# JdbcDaoSupport
public class OrderDaoImpl extends JdbcDaoSupport implements OrderRepositoryCustom {
private final OrderSql sqls;
public OrderDaoImpl(EntityJdbcProvider entityJdbcProvider) {
this.sql = super.sqls(OrderSql::new);
}
@Override
public List<OrderDto> selectByPurchaserNo(String purchaserNo) {
String sql = this.sql.selectByPurchaserNo();
return select(sql, mapParameterSource()
.addValue("purchaserNo", purchaserNo),
OrderDto.class);
}
}Repository μ Dao λ₯Ό κ°λ μ μΌλ‘ λΆλ¦¬ν΄μ μ¬μ©νλλ‘ κ°κ° μ 곡ν©λλ€. JdbcRepositorySupport μ JdbcDaoSupport κ° μ 곡νλ κΈ°λ₯μ μ μ¬νμ§λ§, ꡬν κ΄μ μμ μ°¨μ΄κ° μμ΅λλ€.
(1) μ‘°ν μ€ν λ©μλ λͺ
- JdbcRepositorySupport λ
findλ‘ μ€νν©λλ€. - JdbcDaoSupport λ
selectλ‘ μ€νν©λλ€. - κΈ°λ₯μ μΈ μ°¨μ΄λ μμ΅λλ€.
(2) λ°ν νμ
- JdbcRepositorySupport λ Repository μ λμλλ AggregateRoot(Entity) νμ
μ κΈ°λ³Έ νμ
μΌλ‘ μ¬μ©ν©λλ€.
- μμ±μμμ AggregateRoot νμ μ μ€μ ν©λλ€.
- JdbcDaoSupport λ λ€μν QueryModel μ λ°νν μ μμΌλ―λ‘, κΈ°λ³Έ νμ μ μ§μ νμ§ μκ³ μ¬μ©ν λ νμ μ κ²°μ ν©λλ€.
# JdbcRepositorySupport
public List<Order> findByPurchaserNo(String purchaserNo) {
String sql = this.sql.selectByPurchaserNo();
return find(sql, mapParameterSource()
.addValue("purchaserNo", purchaserNo)); // μμ±μμμ μ€μ ν Order νμ
μ κΈ°λ³ΈμΌλ‘ μ¬μ©νλ€.
}
# JdbcDaoSupport
public List<OrderDto> selectByPurchaserNo(String purchaserNo) {
String sql = this.sql.selectByPurchaserNo();
return select(sql, mapParameterSource()
.addValue("purchaserNo", purchaserNo),
OrderDto.class); // Query μ€νμ λ°ν νμ
μ μ λ¬νλ€.
}- JdbcRepositorySupport λ₯Ό μ¬μ©νλλΌλ, λ€λ₯Έ λ°ν νμ μ μ λ¬ν μ μμ΅λλ€.
(3) μ‘°ν κ²°κ³Ό λ§€ν
- JdbcRepositorySupport λ
AggregateResultSetExtractorλ₯Ό μ¬μ©ν΄μ μ‘°ν κ²°κ³Όλ₯Ό λ§€νν©λλ€.-
AggregateResultSetExtractorλ1 : Nμ‘°ν κ²°κ³Όλ₯Ό λ°ν νμ (Aggregate) κ²°κ³Όμ λ§€νν©λλ€.
-
- JdbcDaoSupport λ
EntityRowMapperλ₯Ό μ¬μ©ν΄μ μ‘°ν κ²°κ³Όλ₯Ό λ§€νν©λλ€.-
EntityRowMapperλ Row λ¨μ κ²°κ³Όλ₯Ό λ§€νν©λλ€.
-
2κ°μ§ λ§€ν λ°©μμ λ°λΌ μμ±ν΄μΌ λλ SQL μλ μ°¨μ΄κ° λ°μν©λλ€.
JdbcRepositorySupport, JdbcDaoSupport λ₯Ό μ¬μ©νλλΌλ λ€λ₯Έ λ°©μμ κ²°κ³Ό λ§€νμ μ¬μ©ν μ μμ΅λλ€.
# JdbcRepositorySupport
public List<Order> findByPurchaserNo(String purchaserNo) {
String sql = this.sql.selectByPurchaserNo();
return find(sql, mapParameterSource()
.addValue("purchaserNo", purchaserNo),
this.getRowMapper()); // μ‘°ν κ²°κ³Όλ₯Ό EntityRowMapper λ‘ λ§€νν©λλ€.
}
# JdbcDaoSupport
public List<OrderDto> selectByPurchaserNo(String purchaserNo) {
String sql = this.sql.selectByPurchaserNo();
return select(sql, mapParameterSource()
.addValue("purchaserNo", purchaserNo),
this.getAggregateResultSetExtractor(OrderDto.class)); // μ‘°ν κ²°κ³Όλ₯Ό AggregateResultSetExtractor λ‘ λ§€νν©λλ€.
}(4) μ‘°ν κ²°κ³Όλ₯Ό AfterLoadEvent, AfterLoadCallback λ‘ λ°ν 4.8. Entity Callbacks
-
JdbcRepositorySupportλ μ‘°ν κ²°κ³Ό Event λ° Callback μApplicationEventPublisher,EntityCallbacksμ λ°νν©λλ€.
-
JdbcDaoSupportλ₯Ό μμνκ³ , Reactive(Flux) μ‘°ν λ©μλλ₯Ό μ 곡νλ€. - JDBC Query μ€νμ BLOCKING μ΄μ§λ§, Excel Download μ κ°μ΄
Asynchronous Streamμ΄ ν¨κ³Όμ μΌ λ μ¬μ©ν μ μλ€. -
CustomRepositoryνμ₯κ³Ό κ°μ΄ μ¬μ©νκΈ° μν΄μλspring-data-jdbc-plus-repositoryμreactive-supportμ΅μ μ΄ νμ±ν λμΌ νλ€.Reactive Type Support
AggregateResultSetExtractor μ EntityRowMapper λ μ‘°ν κ²°κ³Ό λ§€ν λ°©μμ μ°¨μ΄κ° μμΌλ©°, μ°κ΄κ΄κ³κ° μ‘΄μ¬ν κ²½μ° SQL μ JOIN ꡬ문λ μ΄μ λ§μΆ°μ μμ±ν΄μΌ ν©λλ€.
- 1:1, 1:N μ°κ΄κ΄κ³ μν°ν°
@Table("n_board")
public class Board {
@Id
private Long id;
private String name;
@Column("board_id")
private Audit audit; // 1:1 κ΄κ³ (FK Column `n_audit.board_id`)
@MappedCollection(idColumn = "board_id", keyColumn = "board_index")
private List<Post> posts = new ArrayList<>(); // 1:N κ΄κ³ (FK Column `n_post.board_id`, Order By Column `n_post.board_index`)
}
@Table("n_audit")
public class Audit {
@Id
private Long id;
private String name;
}
@Table("n_post")
public class Post {
@Id
private Long id;
private String title;
private String content;
}- 1:1 κ΄κ³(n_board -> b_audit)λ JOIN νλλΌλ, κΈ°μ€ ν μ΄λΈ "n_board" μ ROW κ²°κ³Όκ° μ€λ³΅λμ§ μμ΅λλ€.
- 1:N κ΄κ³(n_board -> n_post)λ JOIN νμ λ, n_post μ μ°κ²° κ°―μλ§νΌ κΈ°μ€ ν
μ΄λΈ "n_board" μ ROW κ²°κ³Όκ° μ€λ³΅λ©λλ€.
(μ°κ΄κ΄κ³ λ°μ΄ν°κ° μμ μλ μλ€λ©΄,
LEFT OUTER JOINμΌλ‘ μμ±ν΄μΌ ν©λλ€.)
Spring Data JDBC μ CrudRepository μμ μ¬μ©νλ RowMapper μ λλ€.
-
EntityRowMapper λ SQL μ‘°ν κ²°κ³Όλ₯Ό ROW λ¨μλ‘ 1:1 μ°κ΄κ΄κ³κΉμ§ λ§€νν©λλ€.
-
1:N μ°κ΄κ΄κ³λ μΆκ° SQL μ μ€ν(EAGER Fetch)ν΄μ κ²°κ³Όλ₯Ό λ§€νν©λλ€.
-
SQL μ μ§μ μμ±ν λ, 1:1 μ°κ΄κ΄κ³κΉμ§
SELECTꡬ문과FROMꡬ문(JOIN ν¬ν¨)μ μμ±ν΄μ€μΌ ν©λλ€. -
μ€ν SQL μμ±
SELECT n_board.id AS id, n_board.name AS name, audit.id AS audit_id, audit.name AS audit_name
FROM n_board
LEFT OUTER JOIN n_audit AS audit
WHERE id = :id-
n_postκ³Όμ 1:N μ°κ΄κ΄κ³λ μΆκ° SQL μ΄ μλμΌλ‘ μ€νλλ©΄μ κ²°κ³Όκ° λ§€νλ©λλ€.
SELECT n_post.id AS id, n_post.title AS title, n_post.content AS content
FROM n_post
WHERE n_post.board_id = :board_id
ORDER BY board_index- Spring Data JDBC CrudRepository λμκ³Ό λμΌνλ©°,
N+1μΏΌλ¦¬κ° μ€νλ μ μμ΅λλ€.
SELECT μ‘°ν κ²°κ³Ό μ€ 1:N κ΄κ³ κ²°κ³ΌκΉμ§ Grouping ν΄μ κ²°κ³Ό κ°μ²΄μ λ§€νν©λλ€.
-
@EntityGraphμ λΉμ·νκ² EAGER Fetch μμ΄ νλ²μ μ‘°νν κ²°κ³Όλ₯Ό λ§€νν©λλ€. -
1:NLEFT OUTER JOIN κ²°κ³Όλ₯Ό1:Nκ²°κ³Ό κ°μ²΄μ Grouping ν΄μ λ§€νν©λλ€.
| Board | Audit | Post |
|---|---|---|
| Board 1 | Audit 1 | Post 1 |
| Board 1 | Audit 1 | Post 2 |
| Board 1 | Audit 1 | Post 3 |
| Board 2 | Audit 2 | Post 4 |
| Board 2 | Audit 2 | Post 5 |
# λ§€ν κ²°κ³Ό
1. Board 1 / Audit 1 / Post 1, Post 2
2. Board 2 / Audit 2 / Post 4, Post 5
-
AggregateResultSetExtractorλ₯Ό μ¬μ©νκΈ° μν΄μ Grouping Entity λ@Idμ»¬λΌ νλλ νμμ λλ€.
SQL μμ±μ μ§μνλ SqlProvider λ₯Ό μ£Όμ
λ°μμ μ 곡ν©λλ€.
SqlProvider λ columns, tables, aggregateColumns, aggregateTables λ©μλλ₯Ό μ 곡νλ€.
- JdbcRepositorySupport, JdbcDaoSupport μ
sqlsλ©μλλ₯Ό μ¬μ©νμ¬,SqlProviderλ₯Ό μ£Όμ λ°μ μ μμ΅λλ€.
public class BoardRepositoryImpl extends JdbcRepositorySupport<Board> implements BoardRepositoryCustom {
private final BoardSql sqls;
public BoardRepositoryImpl(EntityJdbcProvider entityJdbcProvider) {
super(Board.class, entityJdbcProvider);
this.sql = super.sqls(BoardSql::new); // BoardSql μμ± λ° SqlProvider κ°μ²΄ μ£Όμ
}
}SQL μμ± ν΄λμ€λ Groovy λ Kotlin κ³Ό κ°μ΄ MultiLine String μ μ§μνλ μΈμ΄μ λμμ λ°μ μ μλ€.
Java 13 JEP 355: Text Blocks(Preview), Java 14 JEP 368: Text Blocks(Second Preview) λ₯Ό νμ©ν μλ μλ€.
# Groovy
class BoardSql extends SqlGeneratorSupport {
/**
* SELECT n_board.id AS id, n_board.name AS name, audit.id AS audit_id, audit.name AS audit_name
* FROM n_board
* LEFT OUTER JOIN n_audit AS audit
* WHERE name = :name
*/
String selectByName() {
"""
SELECT ${sql.columns(Board)}
FROM ${sql.tables(Board)}
WHERE name = :name
"""
}
/**
* SELECT n_board.id AS id, n_board.name AS name, audit.id AS audit_id, audit.name AS audit_name, post.id AS post_id, post.title AS post_title, post.content AS post_content
* FROM n_board
* LEFT OUTER JOIN n_audit AS audit
* LEFT OUTER JOIN n_post AS post
* WHERE name = :name
*/
String selectAggregateByName() {
"""
SELECT ${sql.aggregateColumns(Board)}
FROM ${sql.aggregateTables(Board)}
WHERE name = :name
"""
}
}-
${sql.columns(Board)}:1:1μ°κ΄κ΄κ³μ ν΄λΉνλ SELECT Column ꡬ문μ μΆλ ₯νλ€. (EntityRowMapper μ λμ) -
${sql.tables(Board)}:1:1μ°κ΄κ΄κ³μ ν΄λΉνλ FROM JOIN ꡬ문μ μΆλ ₯νλ€. (EntityRowMapper μ λμ) -
${sql.aggregateColumns(Board)}:1:Nμ°κ΄κ΄κ³λ₯Ό ν¬ν¨ν SELECT Column ꡬ문μ μΆλ ₯νλ€. (AggregateResultSetExtractor μ λμ) -
${sql.aggregateTables(Board)}:1:Nμ°κ΄κ΄κ³λ₯Ό ν¬ν¨ν FROM JOIN ꡬ문μ μΆλ ₯νλ€. (AggregateResultSetExtractor μ λμ)
-
JdbcOperationsμμ SQL νλΌλ―Έν°λ₯Ό λ°μΈλ©νκΉ μν΄ SqlParameterSource λ₯Ό μ 곡ν΄μΌ ν©λλ€. - SqlParameterSource λ
beanParameterSource,mapParameterSource,entityParameterSource,compositeSqlParameterSourceλ₯Ό μ 곡ν©λλ€.
# JdbcRepositorySupport
public List<Order> find(OrderCriteria criteria) {
String sql = this.sql.select();
return find(sql, beanParameterSource(criteria)); // beanParameterSource
}
public List<Order> findByPurchaserNo(String purchaserNo) {
String sql = this.sql.selectByPurchaserNo();
return find(sql, mapParameterSource()
.addValue("purchaserNo", purchaserNo)); // mapParameterSource
}
public List<Order> findByExample(Order order) {
String sql = this.sql.select();
return find(sql, entityParameterSource(order)); // entityParameterSource
}
public List<Order> findByPurchaserNo(String purchaserNo, OrderCriteria criteria) {
String sql = this.sql.selectExample;
return find(sql, compositeSqlParameterSource(
mapParameterSource().addValue("purchaserNo", purchaserNo),
beanParameterSource(criteria)
); // compositeSqlParameterSource
}- beanParameterSource: parameter κ°μ²΄μ getter λ₯Ό μ¬μ©ν΄μ binding ν μ μλ€.
- mapParameterSource: map μ key/value λ₯Ό νλΌλ―Έν°λ‘ μ λ¬ν΄μ binding ν μ μλ€.
- entityParameterSource: Spring Data JDBC μ λ§€ν μ 보λ₯Ό μ¬μ©ν΄μ νλΌλ―Έν° binding ν μ μλ€. (@Column)
- compositeSqlParameterSource: λ³΅ν© SqlParameterSource λ₯Ό μ‘°ν©ν νλΌλ―Έν°λ‘ μ λ¬ν΄μ binding ν μ μλ€.
count κ²°κ³Όμ κ°μ΄ λ¨μΌ μ»¬λΌ κ²°κ³Όλ₯Ό λ§€ννλ JdbcOperations νΈμΆν λ SingleValueSelectTrait μ μ¬μ©ν μ μμ΅λλ€.
public class OrderRepositoryImpl extends JdbcRepositorySupport<Order>
implements OrderRepositoryCustom, SingleValueSelectTrait {
private final OrderSql sqls;
public OrderRepositoryImpl(EntityJdbcProvider entityJdbcProvider) {
super(Order.class, entityJdbcProvider);
this.sql = super.sqls(OrderSql::new);
}
@Override
public Long countByPurchaserNo(String purchaserNo) {
String sql = this.sql.countByPurchaserNo();
return selectSingleValue(sql, mapParameterSource()
.addValue("purchaserNo", purchaserNo),
Long.class);
}
}class OrderSql extends SqlGeneratorSupport {
String countByPurchaserNo() {
"""
SELECT count(id)
FROM n_order
WHERE purchaser_no = :purchaserNo
"""
}SqlParameterSource (beanParameterSource, mapParameterSource, entityParameterSource) λ₯Ό μμ±νλ€.
DefaultSqlParameterSourceFactory (Default) μ EntityConvertibleSqlParameterSourceFactory κ° μ 곡λλ€.
SqlParameterSourceFactory μ λ±λ‘λ Converter λ±μ μ€μ μ Spring Data JDBC CrudRepository μ λ³λλ‘ μ€μ λ©λλ€.
- κΈ°λ³Έ JDBC Parameter 컨λ²ν μ λ΅ μ¬μ© (Default μ€μ )
- μμ±ν ParameterSource λ JdbcDriver μ Type Converting μ λ΅μ μμ‘΄νλ€.
- λͺκ°μ§ νμ μ λν ParameterSource Type Converter λ±λ‘ μ§μ
@Configuration
public class JdbcConfig extends JdbcPlusSqlConfiguration {
@Bean
@Override
public SqlParameterSourceFactory sqlParameterSourceFactory(
JdbcMappingContext jdbcMappingContext, JdbcConverter jdbcConverter, Dialect dialect) {
return new EntityConvertibleSqlParameterSourceFactory(
this.parameterSourceConverter(),
jdbcMappingContext,
jdbcConverter,
dialect.getIdentifierProcessing());
}
private ConvertibleParameterSourceFactory parameterSourceConverter() {
JdbcParameterSourceConverter converter = new DefaultJdbcParameterSourceConverter();
ConvertibleParameterSourceFactory parameterSourceFactory = new ConvertibleParameterSourceFactory(converter, null);
parameterSourceFactory.setPaddingIterableParam(true);
return parameterSourceFactory;
}
}JdbcParameterSourceConverter, FallbackParameterSource, PaddingIterable μ€μ μ μ μ©ν SqlParameterSource λ₯Ό μμ±νλ€.
- JdbcParameterSourceConverter: ParameterSource Converting μ μ μ©ν Converter λ₯Ό λ±λ‘νλ€.
- FallbackParameterSource: SQL Binding μ νμν Parameter κ° ParameterSource μ μ‘΄μ¬νμ§ μμ λ μ²λ¦¬ν μ λ΅μ μ£Όμ νλ€.
- PaddingIterable: Iterable(List, Set, Collection) νλΌλ―Έν° λ°μΈλ©μ SQL Parsing λΉμ©μ μ€μ΄κΈ° μν΄ λ°μΈλ© νλΌλ―Έν° κ°―μλ₯Ό κ· μΌνκ² μ‘°μ νλ€. νΉν
WHERE IN쑰건μ μ£Όλ‘ μ¬μ©λλ€.-
parameterSourceFactory.setPaddingIterableParam(true);λ‘ padding μ€μ μ νμ±ν ν μ μλ€. -
this.setPaddingIterableBoundaries(...);λ‘ padding scope ν¨ν΄μ μ§μ ν μ μλ€.
default boundaries:
new int[]{0, 1, 2, 3, 4, 8, 16, 32, 50, 100, 200, 300, 500, 1000, 1500, 2000} -
SELECT *
FROM n_order
WHERE id in (:ids)mapParameterSource()
.add("ids", Arrays.asList("1", "2", "3", "4", "5", "6"));
-->
SELECT *
FROM n_order
WEHERE id IN (?, ?, ?, ?, ?, ?, ?, ?)
-->
SELECT *
FROM n_order
WEHERE id IN ("1", "2", "3", "4", "5", "6", "6", "6")
Spring Data JDBC CrudRepository μλ μ μ©λμ§ μμ΅λλ€.
Default Converter κ° λ΄μ₯λμ΄ μμΌλ©°, μΆκ° νμ
Converter, Unwrapper λ₯Ό λ±λ‘ν μ μλ€.
-
Default Converter
- InstantParameterTypeConverter:
Instantνμ μDateλ‘ λ³ννλ€. - LocalDateTimeParameterTypeConverter:
LocalDateTimeνμ μDateλ‘ λ³ννλ€. - LocalDateParameterTypeConverter:
LocalDateνμ μDateλ‘ λ³ννλ€. - ZonedDateTimeParameterTypeConverter:
ZonedDateTimeνμ μDateλ‘ λ³ννλ€. - UuidToStringTypeConverter:
UUIDνμ μStringμΌλ‘ λ³ννλ€. (VARCHAR(36)) - EnumToNameConverter:
ENUMνμ μnameμΌλ‘ λ³ννλ€.
- InstantParameterTypeConverter:
-
Unwrapper
-
AggregateReferenceμ κ°μ΄ Wrapping λ κ°μ Unwrapping ν μ μλ Unwrapper λ₯Ό λ±λ‘ν μ μλ€. - Unwrapper κ° μ μ©λλ©΄, Unwrapping ν κ²°κ³Όλ₯Ό Converter λ‘ νλ² λ λ³ν λμν μ μλ€.
-
Converter μ Unwrapper λ μ νν νμ
μλ§ λ§€μΉλμ μ μ©λλ€.
λ§€μΉ μ‘°κ±΄μ μ§μ μμ±ν νμκ° μλ€λ©΄, ConditionalConverter μ ConditionalUnwrapper λ₯Ό λ±λ‘ν μ μλ€.
matches λ©μλλ₯Ό ꡬννλ©΄ 쑰건μ λ§λ Converter μ Unwrapper κ° μ νλλ€.
Spring Data JDBC CrudRepository μλ μ μ©λμ§ μμ΅λλ€.
@Value
@Builder
@Table("post")
public class PostDto {
@Id
Long id;
@Column
Post post;
@SqlTableAlias("p_labels")
@MappedCollection(idColumn = "board_id")
Set<Label> labels;
}// In custom SQL
val sql = """
SELECT ${sql.aggregateColumns(PostDto::class.java)}
FROM n_post AS post
LEFT OUTER JOIN n_label AS p_labels
ON post.board_id = p_labels.board_id
"""@SqlTableAlias λ SQL μμ table μ AS μ΄λ¦μ μ§μ ν μ μλ κΈ°λ₯μ
λλ€.
Custom SQL Statement λ₯Ό μμ±ν λ μ μ©νκ² μ¬μ©ν μ μμ΅λλ€.
Class, Field, Method μ μ μ©λ μ μμ΅λλ€.
@SqlFunction μ field λλ method μ μ€μ ν μ μλ annotation μ
λλ€.
νΉμ column μ SQL function μΌλ‘ λ§€ννκ³ μΆμ λ μ¬μ©ν μ μμ΅λλ€.
@Table("n_order")
@Getter
@Builder
public class Order {
@Id
private Long id;
@SqlFunction(expressions = {SqlFunction.COLUMN_NAME, "0"})
private Long price;
private OrderStatus status;
private String purchaserNo;
public void complete() {
this.status = OrderStatus.COMPLETED;
}
}μ΄ Entity λ columns λ‘ μλμ SQL μ κΈ°λ³Έμ μΌλ‘ μμ±νκ²λ©λλ€.
`n_order`.`id` AS `id`,
COALESCE(`n_order`.`price`, 0) AS `price`,
`n_order`.`status` AS `status`,
`n_order`.`purchaser_no` AS `purchaser_no`null default λ₯Ό SQL level μμ μ€μ νκ³ μΆμ λ μ μ©ν©λλ€
- Spring Data JDBC μ CrudRepository μ νμ₯ λ° μΆκ° κΈ°λ₯μ μ 곡ν©λλ€.
- Spring Data JDBC μ CrudRepository λ save λ©μλλ₯Ό μ 곡 ν©λλ€. (merge)
-
@Idμμ± μ λ΅μ λ°λΌ insert λ₯Ό μ§μ νΈμΆν νμκ° μμ΅λλ€. - insert / update λ₯Ό μ§μ νΈμΆν λ
JdbcRepositoryλ₯Ό μμν΄μ κΈ°λ₯μ μ 곡ν μ μμ΅λλ€. - Spring Data JDBC μμλ insert / update λ©μλλ₯Ό μ§μ μ 곡ν κ³νμ΄ μμ΅λλ€. DATAJDBC-282
-
spring-data-jdbc-plus-repositorydependency λ₯Ό κ°μ§λ©΄, Entity μ@Tableμ μ μΈν΄μΌ ν©λλ€.
- Gradle
dependencies {
implementation("com.navercorp.spring:spring-boot-starter-data-jdbc-plus-repository:3.3.5")
}- Maven
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>com.navercorp.spring:spring-boot-starter-data-jdbc-plus-repository</artifactId>
<version>3.3.5</version>
</dependency>- Java Codes
@Table("n_order")
@Data
public class Order {
@Id
@Column("order_no")
private Long orderNo;
@Column("price")
private long price;
@Column("purchaser_no")
private String purchaserNo;
}
public interface OrderRepository extends JdbcRepository<Order, Long> {
}
@Service
public class OrderService {
private final OrderRepository repository;
public OrderService(OrderRepository repository) {
this.repository = repository;
}
public Order save(Order order) {
return this.repository.save(order);
}
// JdbcRepository μΆκ°λ insert / update λ©μλλ₯Ό μ§μ μ¬μ©
public Order insert(Order order) {
return this.repository.insert(order);
}
public Order update(Order order) {
return this.repository.update(order);
}
}- Spring Data JDBC μμλ
CrudRepositoryνμ₯ λ©μλ λ°ν νμ μΌλ‘ Reactive(Flux,Mono) νμ μ νμ©νμ§ μλλ€. - κ°λ¨ν μ€μ μΌλ‘ Reactive(
Flux,Mono) νμ μ λ°ννμ μΌλ‘ κ°μ§λ νμ₯ λ©μλλ₯Ό μ μΈν μ μλλ‘ μ§μνλ€.
spring:
data:
jdbc:
plus:
repositories:
reactive-support: truepublic interface OrderRepository extends CrudRepository<Order, Long>, OrderRepositoryCustom {
}
public interface OrderRepositoryCustom {
Flux<Order> findOrders(String purchaserId);
}@Value
@Builder
@Table("article")
static class SoftDeleteArticle {
@Id
Long id;
String contents;
@SoftDeleteColumn.Boolean(valueAsDeleted = "true")
boolean deleted;
}@SoftDeleteColumn μ Soft Delete κΈ°λ₯μ μν΄ λ§λ€μ΄μ‘μ΅λλ€
spring-data-jdbc μ default DELETE κΈ°λ₯μ override νμ¬
ν΄λΉ νλμ λν update λ₯Ό μ€ννκ² λ©λλ€.
boolean, String type μ μ§μνκ³ μμΌλ©° κ°κ°
@SoftDeleteColumn.Boolean μ΄λ @SoftDeleteColumn.String μ μ¬μ©νμ¬ μΈν
ν μ μμ΅λλ€.
Spring Data JDBC Reference Documentation
1.1. Gradle / Maven Dependency
1.2. JdbcRepositorySupport, JdbcDaoSupport
1.4. EntityRowMapper, AggregateResultSetExtractor
1.4.1. EntityRowMapper
1.4.2. AggregateResultSetExtractor
1.5. SqlGeneratorSupport (SqlAware)
1.6. SqlParameterSource
1.8. SqlParameterSourceFactory
1.8.1. DefaultSqlParameterSourceFactory
1.8.2. EntityConvertibleSqlParameterSourceFactory
1.8.3. ConvertibleParameterSourceFactory
1.8.4. JdbcParameterSourceConverter (DefaultJdbcParameterSourceConverter)
2.1. JdbcRepository