Skip to content

Commit 43d5650

Browse files
committed
Add custom query update support (#151)
Reviewed by @chanhyeong @taes-k * Add custom query update support * Reword to save * Add comments & guide tests * Fix test * Add javadocs for triggerBeforeSave * Arrange update SQL * Remove redundant import
1 parent 1a1cae5 commit 43d5650

File tree

10 files changed

+131
-4
lines changed

10 files changed

+131
-4
lines changed

guide-projects/plus-sql-java-groovy-guide/src/main/java/com/navercorp/spring/data/jdbc/plus/sql/guide/config/JdbcConfiguration.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@
1010
import org.springframework.data.convert.ReadingConverter;
1111
import org.springframework.data.jdbc.core.convert.JdbcConverter;
1212
import org.springframework.data.jdbc.core.convert.JdbcCustomConversions;
13+
import org.springframework.data.relational.core.conversion.MutableAggregateChange;
1314
import org.springframework.data.relational.core.dialect.Dialect;
1415
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
16+
import org.springframework.data.relational.core.mapping.event.BeforeSaveCallback;
1517

18+
import com.navercorp.spring.data.jdbc.plus.sql.guide.support.EmptyStringToNullTraits;
1619
import com.navercorp.spring.data.jdbc.plus.sql.parametersource.EntityConvertibleSqlParameterSourceFactory;
1720
import com.navercorp.spring.data.jdbc.plus.sql.parametersource.SqlParameterSourceFactory;
1821
import com.navercorp.spring.jdbc.plus.support.parametersource.ConvertibleParameterSourceFactory;
@@ -22,6 +25,11 @@
2225
@Configuration
2326
public class JdbcConfiguration {
2427

28+
@Bean
29+
public BeforeSaveCallback<EmptyStringToNullTraits> convertEmptyStringToNull() {
30+
return (aggregate, aggregateChange) -> aggregate.emptyStringToNull();
31+
}
32+
2533
@SuppressWarnings("unchecked")
2634
@Bean
2735
@Primary

guide-projects/plus-sql-java-groovy-guide/src/main/java/com/navercorp/spring/data/jdbc/plus/sql/guide/order/Order.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,21 @@
2323
import org.springframework.data.annotation.Id;
2424
import org.springframework.data.relational.core.mapping.Embedded;
2525
import org.springframework.data.relational.core.mapping.Table;
26+
import org.springframework.util.ObjectUtils;
2627

2728
import lombok.Builder;
2829
import lombok.Getter;
2930

3031
import com.navercorp.spring.data.jdbc.plus.sql.annotation.SqlFunction;
32+
import com.navercorp.spring.data.jdbc.plus.sql.guide.support.EmptyStringToNullTraits;
3133

3234
/**
3335
* @author Myeonghyeon Lee
3436
*/
3537
@Table("n_order")
3638
@Getter
37-
@Builder
38-
public class Order {
39+
@Builder(toBuilder = true)
40+
public class Order implements EmptyStringToNullTraits {
3941
@Id
4042
private Long id;
4143

@@ -46,6 +48,8 @@ public class Order {
4648

4749
private String purchaserNo;
4850

51+
private String name;
52+
4953
@Nullable
5054
@Embedded.Nullable(prefix = "discount_")
5155
private Discount discount;
@@ -69,4 +73,11 @@ public record DiscountType(
6973
String type
7074
) {
7175
}
76+
77+
@Override
78+
public Order emptyStringToNull() {
79+
return this.toBuilder()
80+
.name(ObjectUtils.isEmpty(this.name) ? null : this.name)
81+
.build();
82+
}
7283
}

guide-projects/plus-sql-java-groovy-guide/src/main/java/com/navercorp/spring/data/jdbc/plus/sql/guide/order/OrderRepositoryCustom.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,6 @@ public interface OrderRepositoryCustom {
3131
long countByPurchaserNo(String purchaserNo);
3232

3333
List<Order> findByPurchaserNoAndStatusAndPrice(OrderCriteria criteria, Long price);
34+
35+
int updateName(UpdatingOrderNameDto name);
3436
}

guide-projects/plus-sql-java-groovy-guide/src/main/java/com/navercorp/spring/data/jdbc/plus/sql/guide/order/OrderRepositoryImpl.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,9 @@ public List<Order> findByPurchaserNoAndStatusAndPrice(OrderCriteria criteria, Lo
7070
.addValue("price", price)
7171
));
7272
}
73+
74+
@Override
75+
public int updateName(UpdatingOrderNameDto dto) {
76+
return saveOne(this.sqls.updateName(), dto);
77+
}
7378
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.navercorp.spring.data.jdbc.plus.sql.guide.order;
2+
3+
import org.springframework.util.ObjectUtils;
4+
5+
import com.navercorp.spring.data.jdbc.plus.sql.guide.support.EmptyStringToNullTraits;
6+
7+
public record UpdatingOrderNameDto(
8+
Long id,
9+
String name
10+
) implements EmptyStringToNullTraits {
11+
@Override
12+
public EmptyStringToNullTraits emptyStringToNull() {
13+
return new UpdatingOrderNameDto(
14+
id,
15+
ObjectUtils.isEmpty(name) ? null : name
16+
);
17+
}
18+
}

guide-projects/plus-sql-java-groovy-guide/src/main/java/com/navercorp/spring/data/jdbc/plus/sql/guide/order/sql/OrderSql.groovy

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ class OrderSql extends SqlGeneratorSupport {
5555
}
5656

5757
String selectByPurchaserNoAndStatusAndPrice() {
58-
"""
58+
"""
5959
SELECT ${sql.columns(Order)}
6060
FROM ${sql.tables(Order)}
6161
WHERE purchaser_no = :criteria.purchaserNo
@@ -71,4 +71,12 @@ class OrderSql extends SqlGeneratorSupport {
7171
WHERE purchaser_no = :purchaserNo
7272
"""
7373
}
74+
75+
String updateName() {
76+
"""
77+
UPDATE n_order
78+
SET name = :name
79+
WHERE id = :id
80+
"""
81+
}
7482
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.navercorp.spring.data.jdbc.plus.sql.guide.support;
2+
3+
public interface EmptyStringToNullTraits {
4+
EmptyStringToNullTraits emptyStringToNull();
5+
}

guide-projects/plus-sql-java-groovy-guide/src/test/java/com/navercorp/spring/data/jdbc/plus/sql/guide/order/OrderRepositoryTest.java

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.junit.jupiter.api.Test;
3333
import org.springframework.beans.factory.annotation.Autowired;
3434
import org.springframework.boot.test.context.SpringBootTest;
35+
import org.springframework.data.util.StreamUtils;
3536
import org.springframework.data.util.Streamable;
3637
import org.springframework.test.context.ActiveProfiles;
3738
import org.springframework.transaction.annotation.Transactional;
@@ -51,11 +52,13 @@ class OrderRepositoryTest {
5152
.price(1000L)
5253
.status(OrderStatus.PLACE)
5354
.purchaserNo("navercorp")
55+
.name("")
5456
.build(),
5557
Order.builder()
5658
.price(5000L)
5759
.status(OrderStatus.PLACE)
5860
.purchaserNo("navercorp")
61+
.name("yd2")
5962
.discount(Order.Discount.builder()
6063
.type(new Order.DiscountType("coupon"))
6164
.build())
@@ -64,6 +67,7 @@ class OrderRepositoryTest {
6467
.price(3000L)
6568
.status(OrderStatus.COMPLETED)
6669
.purchaserNo("navercorp")
70+
.name("chanwool")
6771
.discount(Order.Discount.builder()
6872
.amount(1000L)
6973
.type(new Order.DiscountType("coupon"))
@@ -73,6 +77,7 @@ class OrderRepositoryTest {
7377
.price(9000L)
7478
.status(OrderStatus.COMPLETED)
7579
.purchaserNo("navercorp")
80+
.name("chanhyeong")
7681
.discount(Order.Discount.builder()
7782
.amount(1000L)
7883
.build()
@@ -106,13 +111,29 @@ void findByPurchaserNo() {
106111
actual.sort(comparing(Order::getPrice));
107112
assertThat(actual).hasSize(4);
108113
assertThat(actual.get(0).getPrice()).isEqualTo(1000L);
114+
assertThat(actual.get(0).getName()).isNull();
109115
assertThat(actual.get(0).getStatus()).isEqualTo(OrderStatus.PLACE);
110116
assertThat(actual.get(1).getPrice()).isEqualTo(3000L);
111117
assertThat(actual.get(1).getStatus()).isEqualTo(OrderStatus.COMPLETED);
112118
assertThat(actual.get(2).getPrice()).isEqualTo(5000L);
113119
assertThat(actual.get(2).getStatus()).isEqualTo(OrderStatus.PLACE);
114120
}
115121

122+
@Test
123+
void updateOrderNameWithBeforeSaveConverter() {
124+
// given
125+
List<Order> samples = StreamUtils.createStreamFromIterator(sut.saveAll(orders).iterator()).toList();
126+
127+
// when
128+
sut.updateName(new UpdatingOrderNameDto(samples.get(2).getId(), ""));
129+
130+
// then
131+
assertThat(samples.get(2).getName()).isNotBlank();
132+
assertThat(sut.findById(samples.get(2).getId())).hasValueSatisfying(actual -> {
133+
assertThat(actual.getName()).isNull();
134+
});
135+
}
136+
116137
@Test
117138
void findByIds() {
118139
// given
@@ -129,7 +150,7 @@ void findByIds() {
129150
// then
130151
assertThat(actual).allSatisfy(it ->
131152
assertThat(it).usingRecursiveComparison()
132-
.ignoringFields("id")
153+
.ignoringFields("id", "name")
133154
.isEqualTo(orderByPrice.get(it.getPrice()))
134155
);
135156
}

guide-projects/plus-sql-java-groovy-guide/src/test/resources/data/schema.sql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ CREATE TABLE IF NOT EXISTS n_order (
33
price INT,
44
status VARCHAR(20),
55
purchaser_no VARCHAR(36),
6+
name VARCHAR(20),
67
discount_amount INT,
78
discount_type VARCHAR(20),
89
PRIMARY KEY(id));

spring-data-jdbc-plus-sql/src/main/java/com/navercorp/spring/data/jdbc/plus/sql/support/JdbcRepositorySupport.java

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,11 @@
3030
import org.springframework.dao.EmptyResultDataAccessException;
3131
import org.springframework.dao.IncorrectResultSizeDataAccessException;
3232
import org.springframework.data.mapping.callback.EntityCallbacks;
33+
import org.springframework.data.relational.core.conversion.MutableAggregateChange;
3334
import org.springframework.data.relational.core.mapping.event.AfterConvertCallback;
3435
import org.springframework.data.relational.core.mapping.event.AfterConvertEvent;
36+
import org.springframework.data.relational.core.mapping.event.BeforeSaveCallback;
37+
import org.springframework.data.relational.core.mapping.event.BeforeSaveEvent;
3538
import org.springframework.jdbc.core.ResultSetExtractor;
3639
import org.springframework.jdbc.core.RowMapper;
3740
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
@@ -487,6 +490,51 @@ protected <R> R requiredOne(
487490
.orElseThrow(() -> new EmptyResultDataAccessException("RequiredOne result must be One.", 1));
488491
}
489492

493+
/**
494+
* Save one r
495+
*
496+
* @param sql the sql
497+
* @param aggregate the aggregate for save
498+
* @return affected row count
499+
* @param <R> the type parameter
500+
*/
501+
protected <R> int saveOne(String sql, R aggregate) {
502+
return this.getJdbcOperations().update(
503+
sql,
504+
beanParameterSource(triggerBeforeSave(aggregate))
505+
);
506+
}
507+
508+
/**
509+
* Save list
510+
*
511+
* @param sql the sql
512+
* @param aggregate the aggregate list for save
513+
* @return affected row count
514+
* @param <R> the type parameter
515+
*/
516+
protected <R> int[] saveBatch(String sql, List<R> aggregate) {
517+
return this.getJdbcOperations().batchUpdate(
518+
sql,
519+
aggregate.stream().map(it -> beanParameterSource(triggerBeforeSave(it))).toArray(SqlParameterSource[]::new)
520+
);
521+
}
522+
523+
/**
524+
* Trigger before save r
525+
*
526+
* @param aggregate the aggregate
527+
* @return the r returned by entity callbacks
528+
* @param <R> the type parameter
529+
*/
530+
protected <R> R triggerBeforeSave(R aggregate) {
531+
this.getApplicationEventPublisher()
532+
.publishEvent(new BeforeSaveEvent<>(aggregate, MutableAggregateChange.forSave(aggregate)));
533+
534+
return this.getEntityCallbacks()
535+
.callback(BeforeSaveCallback.class, aggregate, MutableAggregateChange.forSave(aggregate));
536+
}
537+
490538
/**
491539
* Trigger after load r.
492540
*

0 commit comments

Comments
 (0)