如何持久化仅包含集合和ID的实体?

8

我遇到了一个问题,无法持久化或合并只包含 ID 和其他对象集合的对象。如果我添加另一个字段,则会正常提交事务。同时,如果我将ID生成策略更改为AUTO,也可以起作用。

我的团队其余成员使用“IDENTITY”而不是“AUTO”,所以我想与他们保持一致。他们的实体不仅仅是ID + 集合,所以对他们来说很好用。以下是我想使其工作的内容:

@Entity
public class Filter implements Serializable {

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

   @OneToMany( fetch = FetchType.EAGER, orphanRemoval = true, cascade = { CascadeType.ALL } )
   private ArrayList<Rule> rules = new ArrayList<>();

   public Filter() {

   }

}

错误信息:

org.apache.openjpa.persistence.PersistenceException: ERROR: syntax error at or near ")"
  Position: 25 {prepstmnt 693640431 INSERT INTO Filter () VALUES ()} [code=0, state=42601]
FailedObject: middle.ware.Filter@630cd05

本质上,因为它只是一个ID和Join Table,所以在没有任何字段的情况下尝试持久化或合并过滤器时,它会失败。

解决方案

  • 将GenerationType.IDENTITY更改为GenerationType.AUTO。

    -唯一的缺点似乎是初始起始主键跳动50,然后开始逐一增加。

  • 将GenerationType更改为TABLE

    -这似乎就是AUTO选择的。

  • 向实体添加一个任意字段(即String test = "test")。

    -只需使实体有一个以上的字段即可使其持久化。但是,我不需要这个字段;我只想要一个集合

  • 使关系双向。

    -通过使关系双向,表格具有回溯id(而不仅仅是一个id)。这仅适用于Filter由另一个记录拥有的情况。


如果您的JPA提供程序不支持没有列的INSERT语句,则应该对其进行错误报告。我在使用的提供程序(DataNucleus)中这样做,它能够生成正确的SQL语句。 - Neil Stockton
使用AUTO并不是一个真正的答案,因为AUTO意味着将策略交给提供者来决定,而他们可能会在底层使用IDENTITY,这最终会导致相同的问题。 - Neil Stockton
已从作者中删除 - garfield
@SreenathReddy 如何尝试保留代码并不重要... 实体除了“id”列没有其他列,在INSERT语句中将其归属,因此在INSERT中不存在任何列。 - Neil Stockton
@NeilStockton 我同意你的看法。我只是想看看他是如何做到这一点的,以交叉检查是否在保存数据时漏掉了什么。 - Sreenath Reddy
显示剩余8条评论
2个回答

0
一个好的解决方案应该使代码双向,并利用"@PrePersist"注释将id设置为“Rules”对象。类似这样:

Rule.java

@Entity
public class Rule implements Serializable {

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

    private String description;

    @ManyToOne
    private Filter filter;

    // getters, setters, equals, hashcode, toString...
}

Filter.java

@Entity
public class Filter implements Serializable {

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

    @OneToMany( fetch = FetchType.EAGER, orphanRemoval = true, cascade = { CascadeType.ALL }, mappedBy="filter" )
    private List<Rule> rules = new ArrayList<>();

    @PrePersist
    public void prePersist() {
        if (rules != null) {
            rules.forEach(item -> item.setFilter(this));
        }
    }

    // getters, setters, equals, hashcode, toString...
}

FilterRepository.java

@Repository
public interface FilterRepository extends JpaRepository<Filter, Integer> { }

AnUnitTest.java

@RunWith(SpringRunner.class)
@SpringBootTest
public class AnUnitTest {
    @Autowired
    private FilterRepository filterRepository;

    @After
    public void clearDatabase() {
        bodyPartRespository.deleteAll();

        filterRepository.deleteAll();
    }

    @Test
    public void testFilterRules() {
        Rule aRule = Rule.builder()
                .description("A")
                .build();
        Rule anotherRule = Rule.builder()
                .description("B")
                .build();

        Filter filter = Filter.builder()
                .rules(Arrays.asList(aRule, anotherRule))
                .build();

        filterRepository.saveAndFlush(filter);

        List<Filter> all = filterRepository.findAll();

        all.forEach(System.out::println);
    }
}

对我来说,上面的代码运行良好。

希望这能解决你的问题。

祝好,尼古拉斯。


0

我用4个不同的例子实现了你的问题:

  1. 只有ID

    @Entity public class Case1 implements Serializable {

    private static final long serialVersionUID = 1L;

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

    // ... }

  2. ID + 1个字符串列

    @Entity public class Case2 implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name;

    // ... }

  3. ID + 1个OneToMany关联

a. 带有cascade = { CascadeType.ALL }

> @Entity
> public class Case3 implements Serializable {
>         
> private static final long serialVersionUID = 1L;
>            
> @Id
> @GeneratedValue(strategy = GenerationType.IDENTITY)
> private Long id;
> @OneToMany( fetch = FetchType.EAGER, orphanRemoval = true, cascade = { CascadeType.ALL } )
> private ArrayList<AdminUtilisateur> users = new ArrayList<>();        
>  // ...
>}
无级联 = {CascadeType.ALL}
> @Entity
> public class Case4 implements Serializable {
>         
> private static final long serialVersionUID = 1L;
>            
> @Id
> @GeneratedValue(strategy = GenerationType.IDENTITY)
> private Long id;
>  
> @OneToMany( fetch = FetchType.EAGER, orphanRemoval = true)
> private ArrayList<AdminUtilisateur> users = new ArrayList<>();      
>  // ...
>}

使用 SQL 查询结果良好(在 PostgreSQL 9.3 下测试)。 但是使用 javax.persistence,我得到了不同的结果:

  1. 情况1:异常描述:要插入表 [DatabaseTable(case1)] 的字段列表为空。您必须为此表定义至少一个映射。
  2. 情况2:正常工作。
  3. 情况3:本地异常堆栈:异常 [EclipseLink-4002](Eclipse Persistence Services - 2.4.0.v20120608-r11652): org.eclipse.persistence.exceptions.DatabaseException 内部异常:org.postgresql.util.PSQLException:ERREUR:la valeur d'une clé dupliquée rompt la contrainte unique « rule_code_key »
  4. 情况4:异常描述:要插入表 [DatabaseTable(case4)] 的字段列表为空。您必须为此表定义至少一个映射。

对于情况1、3和4的错误解释很简单。

对于情况3,当你有 cascade = { CascadeType.ALL } 时,orm 尝试在持久化Filter之前持久化Rule,一切都取决于需求,对于我的情况,Rule已经存在。
对于情况1和4,你正在尝试仅在表1中持久化 Filter 的ID和在Join Table 上持久化规则的ID + Filter的ID。这意味着 Filter 只有ID,这是一个设计问题的代名词 = 你必须为此表定义至少一个映射。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接