【问题标题】:Adding an object from Object A into Object B without creating new object ? HIBERNATE将对象 A 中的对象添加到对象 B 中而不创建新对象?休眠
【发布时间】:2017-11-25 20:48:30
【问题描述】:

假设我有两个对象,一个是用户对象,另一个是状态对象。 state 对象基本上是美国的 50 个州,所以它永远不必改变。然而,用户对象有一个用户所在的状态集合。像这样:

@Entity
@Table(name="tbl_users")
class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name="id", unique=true, nullable = false)
    private int id;

    @Column(name="user_name")
    private String name;

    @OneToMany(targetEntity=State.class, orphanRemoval = false)
    @Column(name="states")
    private Collection<State> states;

    //getters and setters
}

States 实体如下所示:

@Entity
@Table(name="tbl_states")
class State {

   @Id
   @Column(name="id", unique=true, nullable=false)
   private int id;

   @Column(name="state")
   private String state;

   // getters and setters
}

添加用户的代码(使用休眠):

public int addUser(User user) {
    em.persist(user);
    em.flush();
    return user.getId();
}

通过 id 获取状态的代码:

public State getStateById(int id) {
    return em.createQuery("SELECT s FROM State s WHERE s.id =:id, State.class)
            .setParameter("id", id)
            .getSingleResult();
}

但是当我尝试创建一个用户并选择几个状态时,我得到一个 PSQLException:

org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "uk_g6pr701i2pcq7400xrlb0hns"
2017-06-21T22:54:35.959991+00:00 app[web.1]:   Detail: Key (states_id)=(5) already exists.

我尝试查找 Cascade 方法以查看是否可以使用任何方法,但 Cascade.MERGE 和 Cascade.PERSIST 似乎做同样的事情,其余的我认为我不需要(REMOVE、DETACH 等) .我的问题是: 如何在不出现该错误的情况下向 User 对象添加状态?

【问题讨论】:

  • 在将状态对象添加到用户对象之前如何检索它们?
  • @AbassA 我为每个对象执行 getStateById() 并将状态对象返回到数组列表中,然后我使用 setter 方法将该数组列表添加给用户
  • 您能否提供处理保存方法的完整代码。为什么你的集合属性顶部有一个@Column,你不需要这个,我想你有一个表 user_state 支持关系?
  • @AbassA 在前端还是后端?
  • 在后端,您用于保存的代码以及您使用 getStateById 方法的位置

标签: java hibernate one-to-many hibernate-mapping cascade


【解决方案1】:

此代码有效:

class Example {

    @Test
    public void workingTest() {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("testPU");
        EntityManager em = emf.createEntityManager();

        // Creating three states
        State alabama = new State(state: 'Alabama');
        State louisiana = new State(state: 'Louisiana');
        State texas = new State(state: 'Texas');
        em.getTransaction().begin();
        em.persist(alabama);
        em.persist(louisiana);
        em.persist(texas);
        em.getTransaction().commit();

        List<State> states = em.createQuery('FROM State').getResultList();

        // Assert only three states on DB
        assert states.size() == 3;

        User userFromAlabama = new User();
        User userFromAlabamaAndTexas = new User();

        em.getTransaction().begin();
        State alabamaFromDB = em.find(State, alabama.getId());
        State texasFromDB = em.find(State, texas.getId());
        userFromAlabama.getStates().add(alabamaFromDB);
        userFromAlabamaAndTexas.getStates().add(alabamaFromDB);
        userFromAlabamaAndTexas.getStates().add(texasFromDB);

        em.persist(userFromAlabama);
        em.persist(userFromAlabamaAndTexas);
        em.getTransaction().commit();

        states = em.createQuery('FROM State').getResultList();

        // Assert only three states on DB again
        assert states.size() == 3;

        // Assert one user
        User userFromDB = em.find(User, userFromAlabama.getId());
        assert userFromDB.getStates().size() == 1;

        userFromDB = em.find(User, userFromAlabamaAndTexas.getId());
        assert userFromDB.getStates().size() == 2;
    }
}

@Entity
@Table(name="tbl_users")
class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id

    @Column(name="user_name")
    private String name

    @ManyToMany
    private Collection<State> states = Lists.newArrayList()

    // Getters and setters
}

@Entity
@Table(name="tbl_states")
class State {

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

    @Column(name="state")
    private String state;
    // Getters and setters
}

您应该将映射更改为 @ManyToMany!

您必须在 DB 上有 3 个表,如下所示: TBL_USERS、TBL_STATES 和 TBL_USERS_TBL_STATES

TBL_USERS_TBL_STATES 表是 Hibernate 在使用 @ManyToMany 注释属性时使用的默认表名。如果要更改 TBL_USERS_TBL_STATES 的表名,也请使用 @JoinTable 注释。请参阅文档here

使用此配置,您应该能够从数据库中获取状态,将其添加到新用户,然后将其持久化。我做了一个单元测试,它有效!

【讨论】:

  • 您是说要删除到@OneToMany 配置吗?
  • 是的,因为你不需要它。您真正需要的是确保中间表“TBL_USERS_TBL_STATES”存在于您的数据库中。 Hibernate 将使用这个表来映射用户的状态(状态集合)!
  • services_id ?这个字段在哪里?
  • 抱歉应该是 state_id
  • org.postgresql.util.PSQLException:错误:重复键值违反唯一约束“uk_g6pr701i2pcq7400xrlb0hns”2017-07-07T19:14:07.462969+00:00 app[web.1]:详细信息:键(state_id)=(2) 已经存在。
【解决方案2】:

在您的情况下,最好使用多线程关联与多线程休眠不生成唯一性约束。

使用 onetoMany 时休眠自动生成方案的行为有点奇怪,但您可以使用此解决方法。

试试这个:

@ManyToMany 
@JoinTable(name = "user_state") 
private List<State> states; 

【讨论】:

  • 这不起作用,它一直在一次创建多个用户?就像我选择 2 个州一样,它会创建 2 个具有相同详细信息的用户(id 除外)
  • 它应该可以工作,也许你的保存逻辑不正确,请在你的帖子中添加你所有业务代码的详细信息,我需要整个图片。从用户实例化或加载直到持久化并刷新到数据库包含 adduser 方法周围的代码。
猜你喜欢
  • 1970-01-01
  • 2015-06-16
  • 1970-01-01
  • 2013-02-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多