Java 向其他实体添加相同的项时,Spring JPA实体正在丢失数据
使用Java 1.8、Spring Boot、JPA,我创建了一个Spring Boot微服务, 其中数据模型(实体关系)遵循此特定的一对多关系:Java 向其他实体添加相同的项时,Spring JPA实体正在丢失数据,java,spring,database,spring-data,jpql,Java,Spring,Database,Spring Data,Jpql,使用Java 1.8、Spring Boot、JPA,我创建了一个Spring Boot微服务, 其中数据模型(实体关系)遵循此特定的一对多关系: Owner can have many Cars. Cars only have one Owner. 此Spring Boot Microservice具有以下功能: HTTP获取端点: 从数据库中获取特定所有者的数据(姓名、地址等) 从数据库中检索特定车主的汽车信息(品牌、型号等) HTTP POST端点: 将有关所有者的数据持久化到数据
Owner can have many Cars.
Cars only have one Owner.
此Spring Boot Microservice具有以下功能:
HTTP获取端点:
- 从数据库中获取特定所有者的数据(姓名、地址等)
- 从数据库中检索特定车主的汽车信息(品牌、型号等)李>
- 将有关所有者的数据持久化到数据库中
- 将有关车主汽车的数据持久保存到数据库中
src/main/resources/data/owner.json:
[
{
"name": "Tom Brady"
},
{
"name": "Kobe Bryant"
},
{
"name": "Mike Tyson"
},
{
"name": "Scottie Pippen"
},
{
"name": "John Madden"
},
{
"name": "Arnold Palmer"
},
{
"name": "Tiger Woods"
},
{
"name": "Magic Johnson"
},
{
"name": "George Foreman"
},
{
"name": "Charles Barkley"
}
]
因此,当我运行此程序时,以下几行被注释掉:
// Populate owner cars one by one
for (int i = 0; i < populatedOwners.size(); i++) {
carService.createCars(populatedOwners.get(i).getId(), cars.get(i));
}
// Provide some owners with multiple cars
// carService.createCars(populatedOwners.get(0).getId(), cars.get(3));
// carService.createCars(populatedOwners.get(0).getId(), cars.get(4));
// carService.createCars(populatedOwners.get(1).getId(), cars.get(3));
但是,当我尝试将更多汽车分配给个人车主时(这似乎会导致其他车主的汽车JSON数组变为空): 如你所见,这些汽车似乎被添加到了汤姆·布雷迪和科贝·布莱恩特的JSON汽车数组中,但从拥有它们的人那里删除了(斯科蒂·皮蓬和约翰·马登现在拥有空的JSON汽车数组) 为什么会发生这种情况,这是我的
carserviceinpl.createCar()
方法可能存在的错误吗
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.myapi</groupId>
<artifactId>car-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>car-api</name>
<description>Car REST API</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
业主单位:
@Entity
@Table(name = "owner")
public class Owner {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotNull
private String name;
@OneToMany(cascade = CascadeType.ALL,
fetch = FetchType.EAGER,
mappedBy = "owner")
private List<Car> cars = new ArrayList<>();
public Owner() {
}
// Getter & Setters omitted for brevity.
}
业主地址:
@Repository
public interface OwnerRepository extends JpaRepository<Owner, Long> {
}
@Repository
public interface CarRepository extends JpaRepository<Car, Long> {
}
@Query("select o from Owner o where o.id = :id left join fetch o.cars")
findOrderWithCars(@Param("id") Long ownerId)
CarServiceImpl:
@Service
public class OwnerServiceImpl implements OwnerService {
@Autowired
OwnerRepository ownerRepository;
@Autowired
CarRepository carRepository;
@Override
public List<Owner> getAllOwners() {
return ownerRepository.findAll();
}
@Override
public boolean createOwner(Owner owner) {
boolean created = false;
if (owner != null) {
ownerRepository.save(owner);
created = true;
}
return created;
}
@Override
public Owner getOwnerByOwnerId(Long ownerId) {
Optional<Owner> owner = null;
if (ownerRepository.existsById(ownerId)) {
owner = ownerRepository.findById(ownerId);
}
return owner.get();
}
}
@Service
public class CarServiceImpl implements CarService {
@Autowired
OwnerRepository ownerRepository;
@Autowired
CarRepository carRepository;
@Override
public boolean createCar(Long ownerId, Car car) {
boolean created = false;
if (ownerRepository.existsById(ownerId)) {
Optional<Owner> owner = ownerRepository.findById(ownerId);
if (owner != null) {
List<Car> cars = owner.get().getCars();
cars.add(car);
owner.get().setCars(cars);
car.setOwner(owner.get());
carRepository.save(car);
created = true;
}
}
return created;
}
}
当我把它设为
fetch=FetchType.LAZY
时,它抛出了以下异常:
2020-03-08 15:18:13,175 ERROR org.springframework.boot.SpringApplication [main] Application run failed
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.myapi.model.User.cars, could not initialize proxy - no Session
at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:606)
at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:218)
at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:585)
at org.hibernate.collection.internal.AbstractPersistentCollection.write(AbstractPersistentCollection.java:409)
at org.hibernate.collection.internal.PersistentBag.add(PersistentBag.java:407)
at org.hibernate.collection.internal.PersistentBag.add(PersistentBag.java:407)
at com.myapi.service.CarServiceImpl.createCar(CarServiceImpl.java:36)
at com.myapi.bootstrap.DataInserter.onApplicationEvent(DataInserter.java:71)
at com.myapi.bootstrap.DataInserter.onApplicationEvent(DataInserter.java:24)
at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172)
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139)
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:403)
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:360)
at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:897)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.finishRefresh(ServletWebServerApplicationContext.java:162)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:553)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:141)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:747)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:315)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1215)
at com.myapi.CarApplication.main(CarApplication.java:12)
这是相关的还是单独的问题?我对JPA有些陌生,所以我想知道是否需要将两个实体中的
cascade=CascadeType.ALL
的值更改为其他值
LazyInitializationException
。使用FetchType.EAGER
进行注释是一个解决方案,但不是一个好的解决方案,因为无论是否需要集合,都将始终获取集合。更好的方法是在存储库中使用例如jpql:
@Repository
public interface OwnerRepository extends JpaRepository<Owner, Long> {
}
@Repository
public interface CarRepository extends JpaRepository<Car, Long> {
}
@Query("select o from Owner o where o.id = :id left join fetch o.cars")
findOrderWithCars(@Param("id") Long ownerId)
旁注:此代码是一个杀手:
if (ownerRepository.existsById(ownerId)) {
Optional<Owner> owner = ownerRepository.findById(ownerId);
if (owner != null) {
List<Car> cars = owner.get().getCars();
cars.add(car);
owner.get().setCars(cars);
car.setOwner(owner.get());
carRepository.save(car);
created = true;
}
}
if(ownerRepository.existsById(ownerId)){
可选owner=ownerRepository.findById(ownerId);
如果(所有者!=null){
List cars=owner.get().getCars();
cars.add(car);
owner.get().setCars(cars);
car.setOwner(owner.get());
carRepository.save(car);
创建=真;
}
}
首先,检查数据库中是否存在该实体,如果存在,则向数据库发射另一枪以获取该实体。它可以在一次往返中完成。另一个问题是,您正在检查
可选
是否为空
<代码>可选应永远不为空
。您可能想编写owner.isPresent()
数据插入器的问题在于,它使用相同的汽车对象将其重新分配给不同的车主。如果我们想将多辆车分配给车主,我们需要比车主拥有更多的车对象。我们需要克隆汽车对象以创建具有相同属性的不同汽车对象
我们可以在Car类中编写copy构造函数,它接受现有的Car并返回具有相同属性的Car的副本或克隆。因此,我们可以使用现有的汽车对象创建新的汽车对象。例如,如下所示
Car newCarObject = new Car(existingCarObject);
并修改以下代码以使用现有汽车对象创建新汽车对象
// Provide some owners with multiple cars
// carService.createCars(populatedOwners.get(0).getId(), new Car(cars.get(3)));
// carService.createCars(populatedOwners.get(0).getId(), new Car(cars.get(4)));
// carService.createCars(populatedOwners.get(1).getId(), new Car(cars.get(3)));
对于问题3,集成测试和模拟数据,您应该看看project,特别是MySql模块,您是否可以发布您的数据库模型?如何定义汽车和车主之间的关系?
@Service
public class CarServiceImpl implements CarService {
@Autowired
OwnerRepository ownerRepository;
@Autowired
CarRepository carRepository;
@Override
public boolean createCar(Long ownerId, Car car) {
boolean created = false;
if (ownerRepository.existsById(ownerId)) {
Optional<Owner> owner = ownerRepository.findById(ownerId);
if (owner != null) {
List<Car> cars = owner.get().getCars();
cars.add(car);
owner.get().setCars(cars);
car.setOwner(owner.get());
carRepository.save(car);
created = true;
}
}
return created;
}
}
@RestController
public class OwnerController {
private HttpHeaders headers = null;
@Autowired
OwnerService ownerService;
public OwnerController() {
headers = new HttpHeaders();
headers.add("Content-Type", "application/json");
}
@RequestMapping(value = { "/owners" }, method = RequestMethod.POST, produces = "APPLICATION/JSON")
public ResponseEntity<Object> createOwner(@Valid @RequestBody Owner owner) {
boolean isCreated = ownerService.createOwner(owner);
if (isCreated) {
return new ResponseEntity<Object>(headers, HttpStatus.OK);
}
else {
return new ResponseEntity<Object>(HttpStatus.NOT_FOUND);
}
}
@RequestMapping(value = { "/owners" }, method = RequestMethod.GET, produces = "APPLICATION/JSON")
public ResponseEntity<Object> getAllOwners() {
List<Owner> owners = ownerService.getAllOwners();
if (owners.isEmpty()) {
return new ResponseEntity<Object>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<Object>(owners, headers, HttpStatus.OK);
}
@RequestMapping(value = { "/owners/{ownerId}" }, method = RequestMethod.GET, produces = "APPLICATION/JSON")
public ResponseEntity<Object> getOwnerByOwnerId(@PathVariable Long ownerId) {
if (null == ownerId || "".equals(ownerId)) {
return new ResponseEntity<Object>(HttpStatus.NOT_FOUND);
}
Owner owner = ownerService.getOwnerByOwnerId(ownerId);
return new ResponseEntity<Object>(owner, headers, HttpStatus.OK);
}
}
@RestController
public class CarController {
private HttpHeaders headers = null;
@Autowired
CarService carService;
public CarController() {
headers = new HttpHeaders();
headers.add("Content-Type", "application/json");
}
@RequestMapping(value = { "/cars/{ownerId}" }, method = RequestMethod.POST, produces = "APPLICATION/JSON")
public ResponseEntity<Object> createCarBasedOnOwnerId(@Valid @RequestBody Car car, Long ownerId) {
boolean isCreated = carService.createCar(ownerId, car);
if (isCreated) {
return new ResponseEntity<Object>(headers, HttpStatus.OK);
}
else {
return new ResponseEntity<Object>(HttpStatus.NOT_FOUND);
}
}
@OneToMany(cascade = CascadeType.ALL,
fetch = FetchType.EAGER,
mappedBy = "owner")
private List<Car> cars = new ArrayList<>();
2020-03-08 15:18:13,175 ERROR org.springframework.boot.SpringApplication [main] Application run failed
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.myapi.model.User.cars, could not initialize proxy - no Session
at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:606)
at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:218)
at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:585)
at org.hibernate.collection.internal.AbstractPersistentCollection.write(AbstractPersistentCollection.java:409)
at org.hibernate.collection.internal.PersistentBag.add(PersistentBag.java:407)
at org.hibernate.collection.internal.PersistentBag.add(PersistentBag.java:407)
at com.myapi.service.CarServiceImpl.createCar(CarServiceImpl.java:36)
at com.myapi.bootstrap.DataInserter.onApplicationEvent(DataInserter.java:71)
at com.myapi.bootstrap.DataInserter.onApplicationEvent(DataInserter.java:24)
at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172)
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139)
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:403)
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:360)
at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:897)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.finishRefresh(ServletWebServerApplicationContext.java:162)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:553)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:141)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:747)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:315)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1215)
at com.myapi.CarApplication.main(CarApplication.java:12)
@Query("select o from Owner o where o.id = :id left join fetch o.cars")
findOrderWithCars(@Param("id") Long ownerId)
if (ownerRepository.existsById(ownerId)) {
Optional<Owner> owner = ownerRepository.findById(ownerId);
if (owner != null) {
List<Car> cars = owner.get().getCars();
cars.add(car);
owner.get().setCars(cars);
car.setOwner(owner.get());
carRepository.save(car);
created = true;
}
}
Car newCarObject = new Car(existingCarObject);
// Provide some owners with multiple cars
// carService.createCars(populatedOwners.get(0).getId(), new Car(cars.get(3)));
// carService.createCars(populatedOwners.get(0).getId(), new Car(cars.get(4)));
// carService.createCars(populatedOwners.get(1).getId(), new Car(cars.get(3)));