2016-04-21 93 views
0

我有2个表格:UserLoan。用户有3个字段:id(PK),first_namelast_nameLoan表有场user_id是外键User表: enter image description hereJPA多对一关系CascadeType行为

我的应用程序的逻辑:当我创造新的Loan对象,并设置有相应的user应建立新的独特的用户一个新的或设置iduser纳入现有用户的user_id

为此,我的数据库在first_name,last_name上有一个唯一索引。

Loan类使用User@ManyToOne关系:

public class Loan { 
    @Id 
    @GeneratedValue(strategy = GenerationType.IDENTITY) 
    private Integer id; 
    @ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE}) 
    @JoinColumn(name = "user_id") 
    private User user; 
... methods ... 

当我添加新用户,一切都很好,它坚持用新的PK为DB。但是,当我尝试添加一个现有的我和异常:

javax.servlet.ServletException: org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.6.0.v20150309-bf26070): org.eclipse.persistence.exceptions.DatabaseException 
Internal Exception: java.sql.SQLIntegrityConstraintViolationException: The statement was aborted because it would have caused a duplicate key value in a unique or primary key constraint or unique index identified by 'FIRST_LAST_NAME' defined on 'USER'. 
Error Code: 20000 

当我再次把@ManyToOne(cascade = CascadeType.MERGE)我只能够进入现有的用户,因为只有我通过未在DB我坚持一个新的得到一个例外:

javax.servlet.ServletException: org.springframework.dao.InvalidDataAccessApiUsageException: During synchronization a new object was found through a relationship that was not marked cascade PERSIST: User{id=null, firstName='a', lastName='a'}.; 

@ManyToOne(cascade = CascadeType.ALL)没有工作。

UPDATE

插入代码的新Loan是:

user = userService.findByName(firstName, lastName); 
     if (user == null) { 
      user = new User(firstName, lastName); 
     } 
     loan.setUser(user); 
     loanService.save(loan); 

findByName()save()方法是:

private EntityManager em;  
public User findByName(String firstName, String lastName) { 
      TypedQuery<User> query = em.createQuery(
        "SELECT u FROM User u WHERE u.firstName = :firstName " + 
          "AND u.lastName = :lastName", User.class) 
        .setParameter("firstName", firstName).setParameter("lastName", lastName); 
      List<User> users = query.getResultList(); 
      if (!users.isEmpty()) { 
       return users.iterator().next(); 
      } else { 
       return null; 
      } 
     } 

    public void save(User user) { 
      if (user.getId() == null) { 
       em.persist(user); 
      } else { 
       em.merge(user); 
      } 
     } 
+1

向我们显示您的代码插入。 –

+0

请张贴用户类的代码。 – LearningPhase

+0

我更新了问题,谢谢 – DimaSan

回答

1

如果调用级联的贷款 一)坚持。用户关系和参考我们如果用户实例没有从同一个EntityManager实例/上下文中读入,你会得到一个异常 - 你正在通过userService读取它,然后将贷款保存在loanService中,所以上下文必须被容器管理并在相同的交易中工作。 b)如果未设置cascade.PERSIST(或cascade.ALL),并且引用用户是新的,则会出现“找到新对象”异常。

如果您不能在同一个EntityManager中执行用户读取操作,那么您将要保存贷款,切换到使用合并可能会有所帮助,因为JPA应该随后检查所引用的实体并进行合适的合并。然后,您需要在合并的关系上设置cascade.MERGE选项,以将其应用于用户实例。

请注意,使用合并不同于Persist,因为您传递的内容不受上下文管理。这意味着在事务提交之后,传入的实体仍然没有设置任何主键值。如果您要继续将实体用于任何其他操作,您可能想要传回从合并中返回的实体。

+0

谢谢克里斯的详细解答。我认为你是对的 - 问题是使用了2个不同的EntityManager实例。我会尝试在同一个实例中执行操作。 – DimaSan