这是 insert/update 的 Spring Data JDBC 问题的正确解决方案吗?
Is this a correct solution to the Spring Data JDBC problem of the insert/update?
问题:
当我尝试使用 Spring 数据 JDBC 创建新实体 'Customer' 时(在 Spring-boot 应用程序中)
@Data
public class Customer {
@Id
private String identifier;
private String name;
}
使用客户存储库:
@Repository
public interface CustomerRepository extends CrudRepository<Customer, String> {
}
为了测试所以:
@Test
public void givenNewCustomer_shouldSaveCustomerInDataBase() {
//given
final Customer newCustomer = new Customer();
newCustomer.setIdentifier("0002");
newCustomer.setName("juan");
//when
Customer customerSaved = repository.save(newCustomer);
//then
then(customerSaved).isNotNull();
}
我收到这个错误:
Caused by: org.springframework.dao.IncorrectUpdateSemanticsDataAccessException: Failed to update entity [Customer(identifier=0002, name=juan)]. Id [0002] not found in database.
at org.springframework.data.jdbc.core.JdbcAggregateChangeExecutionContext.updateWithoutVersion(JdbcAggregateChangeExecutionContext.java:370)
at org.springframework.data.jdbc.core.JdbcAggregateChangeExecutionContext.executeUpdateRoot(JdbcAggregateChangeExecutionContext.java:115)
at org.springframework.data.jdbc.core.AggregateChangeExecutor.execute(AggregateChangeExecutor.java:70)
我使用 postgresql 数据库的测试容器和这个初始脚本:
create table customer(
identifier varchar(30) primary key,
name varchar(39));
insert into customer(identifier,name) values ('0001','kevin');
我希望持久化方式表现得像 JPA 中的 EntityManager.merge ()
自动,有没有办法做到这一点?我有一个不知道是不是最正确的
(完整的源代码(非常基本和直接)可用 https://github.com/FabianSR/spring_data_jdbc_example)
建议的解决方案:
我已经针对这个问题实施了一个可能的解决方案,但我不知道它是否是正确的方法(不使用 JPA)。
我已经让 Customer class 扩展了一个实现 Persistable 接口的 class,我已经实现了它的 getId () 方法,以便它在 Customer 字段中搜索是用@Id 和 returns 注释它的值。我还添加了默认值为 true 的标志 isNew。
package com.example.model.core;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient;
import org.springframework.data.domain.Persistable;
import java.util.stream.Stream;
@Data
public abstract class AbstractEntity<I> implements Persistable<I> {
@Transient
public boolean isNew = true;
@Override
public I getId() {
return Stream.of(this.getClass().getDeclaredFields())
.filter(field -> field.isAnnotationPresent(Id.class))
.map(field -> {
field.setAccessible(true);
return field;
}).findFirst().map(field ->
{
try {
return (I) field.get(this);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}).orElse(null);
}
}
而客户如下:
package com.example.model;
import com.example.model.core.AbstractEntity;
import lombok.Data;
import org.springframework.data.annotation.Id;
@Data
public class Customer extends AbstractEntity<String> {
@Id
private String identifier;
private String name;
}
现在我在CustomerRepository接口中添加了一个新的方法,在保存一个Customer之前,它首先搜索它是否存在,修改它的isNew属性并保存它(否则就保存它)
package com.example.repository;
import com.example.model.Customer;
import org.springframework.data.jdbc.repository.query.Modifying;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface CustomerRepository extends CrudRepository<Customer, String> {
@Modifying
default Customer merge(final Customer customer) {
return this.findById(customer.getId()).map(
c -> {
customer.setNew(false);
return this.save(customer);
}
).orElse(this.save(customer));
}
}
最后,在更改测试后,调用新的 'merge' 方法而不是 'save',它已经可以工作了:
@Test
public void givenNewCustomer_shouldSaveCustomerInDataBase() {
//given
final Customer newCustomer = new Customer();
newCustomer.setIdentifier("0002");
newCustomer.setName("juan");
//when
Customer customerSaved = repository.merge(newCustomer);
//then
then(customerSaved).isNotNull();
}
@Test
public void givenOldCustomer_shouldUpdateCustomerInDataBase() {
//Given
final Customer oldCustomer = repository.findById("0001").orElseThrow(AssertionError::new);
oldCustomer.setName(oldCustomer.getName() + " hall");
//when
repository.merge(oldCustomer);
//then
then(repository.findById("0001").map(Customer::getName).map("kevin hall"::equals).orElse(false)).isTrue();
}
(代码在分支 https://github.com/FabianSR/spring_data_jdbc_example/tree/proposed_solution)
这是最好的解决方案还是有更简单的解决方案?
您遇到的错误可能是您在保存前手动设置了ID造成的。尝试在没有标识符的情况下保存数据,以便稍后自动分配值,您将能够使用 repository.save()
.
毫无问题地进行更新
问题:
当我尝试使用 Spring 数据 JDBC 创建新实体 'Customer' 时(在 Spring-boot 应用程序中)
@Data
public class Customer {
@Id
private String identifier;
private String name;
}
使用客户存储库:
@Repository
public interface CustomerRepository extends CrudRepository<Customer, String> {
}
为了测试所以:
@Test
public void givenNewCustomer_shouldSaveCustomerInDataBase() {
//given
final Customer newCustomer = new Customer();
newCustomer.setIdentifier("0002");
newCustomer.setName("juan");
//when
Customer customerSaved = repository.save(newCustomer);
//then
then(customerSaved).isNotNull();
}
我收到这个错误:
Caused by: org.springframework.dao.IncorrectUpdateSemanticsDataAccessException: Failed to update entity [Customer(identifier=0002, name=juan)]. Id [0002] not found in database.
at org.springframework.data.jdbc.core.JdbcAggregateChangeExecutionContext.updateWithoutVersion(JdbcAggregateChangeExecutionContext.java:370)
at org.springframework.data.jdbc.core.JdbcAggregateChangeExecutionContext.executeUpdateRoot(JdbcAggregateChangeExecutionContext.java:115)
at org.springframework.data.jdbc.core.AggregateChangeExecutor.execute(AggregateChangeExecutor.java:70)
我使用 postgresql 数据库的测试容器和这个初始脚本:
create table customer(
identifier varchar(30) primary key,
name varchar(39));
insert into customer(identifier,name) values ('0001','kevin');
我希望持久化方式表现得像 JPA 中的 EntityManager.merge () 自动,有没有办法做到这一点?我有一个不知道是不是最正确的
(完整的源代码(非常基本和直接)可用 https://github.com/FabianSR/spring_data_jdbc_example)
建议的解决方案:
我已经针对这个问题实施了一个可能的解决方案,但我不知道它是否是正确的方法(不使用 JPA)。
我已经让 Customer class 扩展了一个实现 Persistable 接口的 class,我已经实现了它的 getId () 方法,以便它在 Customer 字段中搜索是用@Id 和 returns 注释它的值。我还添加了默认值为 true 的标志 isNew。
package com.example.model.core;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient;
import org.springframework.data.domain.Persistable;
import java.util.stream.Stream;
@Data
public abstract class AbstractEntity<I> implements Persistable<I> {
@Transient
public boolean isNew = true;
@Override
public I getId() {
return Stream.of(this.getClass().getDeclaredFields())
.filter(field -> field.isAnnotationPresent(Id.class))
.map(field -> {
field.setAccessible(true);
return field;
}).findFirst().map(field ->
{
try {
return (I) field.get(this);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}).orElse(null);
}
}
而客户如下:
package com.example.model;
import com.example.model.core.AbstractEntity;
import lombok.Data;
import org.springframework.data.annotation.Id;
@Data
public class Customer extends AbstractEntity<String> {
@Id
private String identifier;
private String name;
}
现在我在CustomerRepository接口中添加了一个新的方法,在保存一个Customer之前,它首先搜索它是否存在,修改它的isNew属性并保存它(否则就保存它)
package com.example.repository;
import com.example.model.Customer;
import org.springframework.data.jdbc.repository.query.Modifying;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface CustomerRepository extends CrudRepository<Customer, String> {
@Modifying
default Customer merge(final Customer customer) {
return this.findById(customer.getId()).map(
c -> {
customer.setNew(false);
return this.save(customer);
}
).orElse(this.save(customer));
}
}
最后,在更改测试后,调用新的 'merge' 方法而不是 'save',它已经可以工作了:
@Test
public void givenNewCustomer_shouldSaveCustomerInDataBase() {
//given
final Customer newCustomer = new Customer();
newCustomer.setIdentifier("0002");
newCustomer.setName("juan");
//when
Customer customerSaved = repository.merge(newCustomer);
//then
then(customerSaved).isNotNull();
}
@Test
public void givenOldCustomer_shouldUpdateCustomerInDataBase() {
//Given
final Customer oldCustomer = repository.findById("0001").orElseThrow(AssertionError::new);
oldCustomer.setName(oldCustomer.getName() + " hall");
//when
repository.merge(oldCustomer);
//then
then(repository.findById("0001").map(Customer::getName).map("kevin hall"::equals).orElse(false)).isTrue();
}
(代码在分支 https://github.com/FabianSR/spring_data_jdbc_example/tree/proposed_solution)
这是最好的解决方案还是有更简单的解决方案?
您遇到的错误可能是您在保存前手动设置了ID造成的。尝试在没有标识符的情况下保存数据,以便稍后自动分配值,您将能够使用 repository.save()
.