当前位置: 首页 > 知识库问答 >
问题:

设置“持久化”时忽略级联类型合并

酆景辉
2023-03-14

Hy全部

我很难解决次年Spring的jpa问题。假设我有以下简单的数据模型(两个实体之间有单向关系)

@Accessors(chain = true) @Getter @Setter  @NoArgsConstructor @AllArgsConstructor
@MappedSuperclass
public class AbstractEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Version
    private Long version;
}

@Accessors(chain = true) @Getter @Setter  @NoArgsConstructor @AllArgsConstructor
@Entity
public class Entity1 extends AbstractEntity {

    private String name;    
}

@Accessors(chain = true) @Getter @Setter  @NoArgsConstructor @AllArgsConstructor
@Entity
public class Entity2 extends AbstractEntity {

    private String name;
    
    @ManyToOne(cascade={ALL})
    private Entity1 entity1;
}

和下面的管道来存储它们

public interface Entity1Dao extends JpaRepository< Entity1, Long >, JpaSpecificationExecutor< Entity1 > {
    
    Entity1 findByName(String name);
}

public interface Entity2Dao extends JpaRepository< Entity2, Long >, JpaSpecificationExecutor< Entity2 > {
    
    Entity2 findByName(String name);    
}

@Service
public class StoreService {

    @Autowired
    Entity1Dao dao1;
    
    @Autowired
    Entity2Dao dao2;
    
    @Transactional
    public Entity1 saveEntity1(Entity1 e) {
        return dao1.save(e);
    }

    @Transactional
    public Entity2 saveEntity2(Entity2 e) {
        return dao2.save(e);
    }

    public Entity1 loadEntity1ByName(String name) {
        return dao1.findByName(name);
    }
}

@SpringBootApplication
public class JpaDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(JpaDemoApplication.class, args);
    }
}

下面的测试

@SpringBootTest
@TestMethodOrder(value = MethodOrderer.OrderAnnotation.class)
class JpaDemoApplicationTests {

    @Autowired
    StoreService store;
    
    @Test
    @Order(1)
    void contextLoads() {
        assertThat(store).isNotNull();
    }

    @Test
    @Order(2)
    void insertEntity1() {
        store.saveEntity1(new Entity1("test entity1"));
        Entity1 saved = store.loadEntity1ByName("test entity1");
        assertThat(saved).isNotNull().hasNoNullFieldsOrProperties();
    }
    
    @Test
    @Order(4)
    void insertEntity2WithNewEntity1() {
        store.saveEntity2(new Entity2("with new entity1", new Entity1("new entity1")));
    }
    
    @Test
    @Order(5)
    void insertEntity2WithExistingEntity1() {
        store.saveEntity2(new Entity2("with saved entity1", store.loadEntity1ByName("test entity1")));
    }
}

最后一次测试(即insertEntity2WithExistingEntity1)失败,出现以下异常

组织。冬眠PersistentObjectException:传递给persist:com的分离实体。实例杰帕德莫。实体1

如果我将Entity2中的CascadeType更改为MERGE,则该测试通过,但insertEntity2WithNewEntity1失败,出现以下异常

org.hibernate.瞬态属性:对象引用未保存的瞬态实例-在刷新之前保存瞬态实例:com.example.jpa演示。Entity2.entity1-

我尝试了多种级联类型的组合,但似乎只要使用PERSIST,最后的测试就会失败(所有测试都包括PERSIST)。

我本以为如果设置了MERGE和PERSIST,它们都会处于活动状态,但从测试来看,设置PERSIST时似乎忽略了MERGE。有什么线索,提示,暗示我做错了什么,这样两个测试都可以运行吗???

编辑

这些测试被认为是模仿REST服务endpoint的行为,转发和保存实体的json rerenentation 1。

第三次测试的json是

{ name: "with new entity1", entity1: { name: "new entity1"}}

第四个的json是

{ name: "with new entity1", entity1: { id: 1, version: 0, name: "test entity1"}}

JPA应该在第三个测试中保留entity1,因为它的id是null,但是应该在第四个测试中合并一个,因为它的id不是null。

然而,我不能两者兼而有之,不是一个就是另一个。

编辑2

我稍微修改了Entity1,以引用与其关联的Entity2列表,并使用@OneToMany和Entity2中相同的级联类型对其进行注释,其行为与Entity2相同。

当我将级联类型设置为MERGE并且仅设置Merge时,我能够保存具有对现有实体的引用的新实体,但不能保存具有对新实体的引用的新实体。

当我将级联类型设置为PERSIST(即PERSIST本身、PERSIST和MERGE或ALL)时,它是相反的;我可以通过引用另一个新实体来保存一个新实体,但我不能通过引用已经存在的新实体来保存一个新实体一。

因此,当设置PERSIST时,它似乎覆盖了MERGE的行为。对我来说,这是一个错误。不是吗?

我已经将源代码上传到github,以防您想亲自进行实验或查看。https://github.com/willix71/persistVsMerge.git

共有2个答案

濮君植
2023-03-14

我不确定这是否相关,但下面的代码正如我所期望的那样工作。删除“cascade=CascadeType.PERSIST”将使持久化测试失败,“对象引用了一个未保存的瞬态实例”。

我还注意到,在您的github repo中,您正在尝试从父级到子级以及子级到父级进行级联。我认为这是你问题的根本原因。

实体:

@Entity
@Table(name = "users")
@Getter
@Setter
@NoArgsConstructor
public class User {

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

    @ManyToOne(cascade = CascadeType.PERSIST)
    Address address;
}

@Entity
@Getter
@Setter
@NoArgsConstructor
public class Address {

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

    @OneToMany(mappedBy = "address")
    List<User> user;
}

存储库:

public interface UserRepository extends JpaRepository<User, UUID> {
}

public interface AddressRepository extends JpaRepository<Address, UUID> {
}

测验:

@DataJpaTest
@Import(DataSourceConfig.class)
class UserRepositoryTest {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private AddressRepository addressRepository;

    @Test
    void testMerge() {
        var address = new Address();
        addressRepository.save(address);

        var user = new User();
        user.setAddress(address);
        userRepository.save(user);

        assertThat(userRepository.findAll()).contains(user);
        assertThat(addressRepository.findAll()).contains(address);
    }  

    @Test
    void testPersist() {
        var address = new Address();

        var user = new User();
        user.setAddress(address);
        userRepository.save(user);

        assertThat(userRepository.findAll()).contains(user);
        assertThat(addressRepository.findAll()).contains(address);
    }
 }
凌啸
2023-03-14

您需要在上次测试中添加@Transactional。加载的实体是分离的,因为没有外部事务,您不能持久化它。

@Test
@Order(5)
@Transactional
void insertEntity2WithExistingEntity1() {
    store.saveEntity2(new Entity2("with saved entity1", store.loadEntity1ByName("test entity1")));
}
 类似资料:
  • 主要内容:JPA级联持久化示例,输出结果级联持久化用于指定如果实体持久化,则其所有关联的子实体也将被持久化。 以下语法用于执行级联持久性操作 - JPA级联持久化示例 在这个例子中,我们将创建两个相互关联的实体类,但要建立它们之间的依赖关系,我们将执行级联操作。 这个例子包含以下步骤 - 第1步: 在包下创建一个名为的实体类,其中包含属性:,,以及标记为级联规范的类型的对象。 文件: StudentEntity.java - 第2步:

  • 我在JPA实体类中有问题。这个实体工作正常,但今天我添加了另一个字段(一个简单的字符串,varchar(255)在数据库中不为NULL)。当我尝试持久化一个新实体时,我得到 嗯,这是真的。该列没有默认值。但在我的代码中,我为它设置了值。查看生成的insert语句时,该字段不存在。 变量的名称与列名匹配,因此没有可能导致错误的注释。它的处理方式与其他工作正常的字段/列一样。 提前感谢您的帮助! 编辑

  • 我有以下问题。我有3个实体,我使用的是一个单向实体: 一个小测试: 因此,看看上面的测试,如果我使用,在事务提交之后,确实会在持久性上下文中获得Entity1的两个实体,如果我将其更改为,则会在持久性上下文中获得Entity1的一个实体。有谁能解释一下为什么会发生这种情况或者指出一些文档吗?

  • TiDB 集群中 PD、TiKV、监控等组件以及 TiDB Binlog 和备份等工具都需要使用将数据持久化的存储。Kubernetes 上的数据持久化需要使用 PersistentVolume (PV)。Kubernetes 提供多种存储类型,主要分为两大类: 网络存储 存储介质不在当前节点,而是通过网络方式挂载到当前节点。一般有多副本冗余提供高可用保证,在节点出现故障时,对应网络存储可以再挂载

  • (译 者注:在阅读本章的时候,以后整个手册的阅读过程中,我们都会面临一个名词方面的问题,那就是“集合”。"Collections" 和 "Set" 在中文里对应都被翻译为“集合”,但是他们的含义很不一样。Collections 是一个超集,Set 是其中的一种。大部分情况下,本译稿中泛指的未加英文注明的“集合”,都应当理解为“Collections”。在有些二者同时出现,可能造成混淆的地 方,我们

  • Serenity 2.1.5 引入保存如下信息的网格列表设置: 可见列和显示顺序 列宽 排序的列 高级过滤器(由右下角的编辑过滤器链接创建) 快速过滤器(撰写本文档时,尚未提供该功能) 包含已删除的状态切换 默认情况下,网格列表不会自动持久化任何东西。 因此,如果你隐藏某些列并离开订单页面,当你再次返回该页面时,你就会看到那些隐藏的列再次成为可见列。 你需要开启所有网格列表的持久化设置,或设置单独