Java 每次使用JPA持久化相同的对象时,都会在数据库中插入一个新行

Java 每次使用JPA持久化相同的对象时,都会在数据库中插入一个新行,java,spring-boot,jpa,spring-data-jpa,Java,Spring Boot,Jpa,Spring Data Jpa,使用javax.persistence.*annotations注释此类: @Entity public class Car{ @Id @GeneratedValue(strategy = GenerationType.AUTO) private Integer id; private Integer weight; public setWeight(weight) { this.weight = weight }

使用
javax.persistence.*
annotations注释此类:

@Entity
public class Car{

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    private Integer weight;

    public setWeight(weight) {
        this.weight = weight
    }
    
    public Integer getWeight() {
        return this.weight;
    }
}
当使用Spring
JpaRepository
interface
save(S entity)
方法将此类的对象X保存在数据库中时,它将生成此对象的Id并保存在数据库中

但是,如果对象X状态发生更改,并使用
save(S entity)
方法再次保存,则不会更新数据库中的旧行,而是创建新行。我猜这种行为是因为在自动生成第一条保存消息返回后,对象id没有被更新(看起来需要一个刷新操作)

所以这是行不通的:

Car myCar = new Car();
carJPARepository.save(myCar); --> New row in Database is inserted;
myCar.setWeight(Integer.valueOf(1000));
carJPARepository.save(myCar); --> New row is inserted in Database instead updated the first one
顺便说一下,
save(S entity)
方法的文档说明:

保存给定的实体。将返回的实例用于进一步的操作,因为保存操作可能已经完全更改了实体实例

这是否意味着我们作为参数传递给save方法的对象与它返回的对象不同,可能具有不同的状态?

Car myCar=new Car();
myCar=carJPARepository.save(myCar);//获取具有更新id的汽车对象
myCar.设定重量(整数值为(1000));
carJPARepository.save(我的车);//调用save现在将更新为java对象作为id

您好,请按照我的示例简单了解流程。 在
save
之前,该实例不受管理,之后则不受管理。因此,对实体所做的所有更改都符合更新操作的条件

点击
dirty save
端点时,您应该会看到类似于以下内容的日志:

Object before save is not assigned default id value and weight is anyway null
Car(id=null, weight=null)
Hibernate: values next value for hibernate_sequence
Hibernate: insert into car (weight, id) values (?, ?)
Plain saved object with default value
Car(id=1, weight=null)
Hibernate: update car set weight=? where id=?
Object with updated weight field
Car(id=1, weight=1000)

如您所见,当您将权重设置为1000时,考虑更新而不是创建新记录。

package com.example.demo;
进口龙目吸气剂;
进口龙目织机;
进口龙目草;
导入org.springframework.beans.factory.annotation.Autowired;
导入org.springframework.data.jpa.repository.JpaRepository;
导入org.springframework.stereotype.Repository;
导入org.springframework.stereotype.Service;
导入org.springframework.transaction.annotation.Transactional;
导入org.springframework.web.bind.annotation.*;
导入javax.persistence.Entity;
导入javax.persistence.GeneratedValue;
导入javax.persistence.GenerationType;
导入javax.persistence.Id;
@RestController
@请求映射(“/cars”)
公共类汽车控制器{
私人终审车服务;
@自动连线
公共车辆控制员(车辆服务){
this.carService=carService;
}
@GetMapping
公共可出租汽车{
返回carService.cars();
}
@邮戳
公共车辆创建(@RequestBody Car){
返回汽车服务。保存(汽车);
}
@后期映射(路径=“脏保存”)
公共汽车dirtyCreate(){
我的车=新车();
System.out.println(“保存前的对象未指定默认id值,且权重为空”);
系统输出打印项次(myCar);
carService.save(myCar);
System.out.println(“带默认值的普通保存对象”);
系统输出打印项次(myCar);
myCar.设定重量(1000);
carService.save(myCar);
System.out.println(“具有更新权重字段的对象”);
系统输出打印项次(myCar);
归还我的车;
}
}
@吸气剂
@塞特
@托斯特林
@实体
班车{
@身份证
@GeneratedValue(策略=GenerationType.AUTO)
私有整数id;
私有整数权重;
}
@服务
等级车服务{
私人最终转帐转帐;
@自动连线
carreservice(CarRepository CarRepository){
this.carRepository=carRepository;
}
@交易的
公共汽车拯救(汽车){
返回存储。保存(汽车);
}
@事务(只读=真)
公共可出租汽车{
return carRepository.findAll();
}
}
@存储库
接口存储扩展了JPA存储{
}

save
方法的返回值分配给
myCar
,以获取托管实例。您可能是在非事务性测试方法中运行此测试,从而导致分离实例,而不是重用持久实例。您是否可以发布控制器和服务代码?@M.Deinum,这在使用JPA存储库时是不必要的;正确的解决方案是修复事务性。我已使用JPARepository和org.springframework.transaction.annotation.Transactional annotation对该类进行了注释,但在保存消息返回后,该对象的id仍然为0。。但是save方法返回的对象是另一个对象(这个对象具有正确的id集)。如果我没有记错的话,persist、update、find和delete是JPA的autogen方法。如果save()方法调用persist(),则每次都会创建一个新行,除非有某种逻辑检查实体中的ID=null并调用update(id),当执行(在同一方法体中):Car myCar=new Car()时,它不工作;carJPARepository.save(myCar);myCar.setWeight(1000);-->在这里,myCar仍然没有新的id设置。通常,使您的
@Controller
`@具有事务性是个坏主意,服务层应该是事务边界,而不是web层。