簡體   English   中英

使用JPA創建數據庫中的唯一並合並現有行

[英]Creating unique and merging existing row in database with JPA

我有2個表: UserLoan User具有3個字段: id (PK), first_namelast_name Loan表的字段user_idUser表的外鍵: 在此處輸入圖片說明

如果他的first_namelast_name是唯一的,則通過保留新的Loan我需要創建一個新的User ,否則將其id放在uder_id

我的Loan類的源代碼:

 public class Loan {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private Long sum;
    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "user_id")
    private User user;

... methods ...

我正在使用這種方法來保留新User

@PersistenceContext
private EntityManager em; 

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

當我嘗試保存新的Loan它總是以first_namelast_name相同但id不同的方式持久保存一個新User

 loan.setSum(sum);
 loan.setUser(new User(firstName, lastName));
 loanService.save(loan);

要使用用戶的first_namelast_name作為PK不是解決方案,我需要一個id

UPDATE_1

我試圖通過User名查找User

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);
        return query.getSingleResult();
    }

但是當我輸入新用戶時,我得到了一個例外:

javax.faces.FacesException: #{loanBean.requestLoan()}: javax.persistence.NoResultException: getSingleResult() did not retrieve any entities.

當我輸入現有用戶時,它將添加一個具有相同firstNamelastName但具有新id新用戶。 當我重復此操作時,我得到另一個異常:

javax.servlet.ServletException: javax.persistence.NonUniqueResultException: More than one result was returned from Query.getSingleResult()

UPDATE_2非常感謝Pietro Boido的寶貴建議。我在DB的first_namelast_name字段上創建了唯一索引,並重構了save()方法。 但是現在當我輸入現有用戶的數據時,我得到了新的例外

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

您應該首先執行查詢以按名稱查找用戶,並僅在未找到用戶時創建一個新用戶:

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

由於可能沒有給定名稱的用戶,因此在查詢用戶時請使用getResultList,因為getSingleResult希望始終找到結果。

List<User> users = query.getResultList();
if (!users.isEmpty()) {
    return users.iterator().next();
} else {
    return null;
}

該代碼假定數據庫在first_name,last_name上具有唯一索引。

您不應該對ManyToOne關系進行級聯操作。 考慮一下:如果您刪除貸款,是否也應該刪除用戶?

當關聯實體是關聯實體的一部分並且它們的生命周期被一起管理時,應該使用級聯操作。

@Entity
@Table(name="loans")
public class Loan implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    @Column(name = "total")
    private Long sum;
    @ManyToOne()
    @JoinColumn(name = "user_id")
    private User user;

    //...
}

這是一個可能的工作示例:

@Stateless
public class LoanService implements LoanServiceRemote {

    @PersistenceContext
    private EntityManager em;

    @Override
    public User createUser(User user) {
        em.persist(user);
        return user;
    }

    @Override
    public Loan createLoan(Loan loan) {
        em.persist(loan);
        System.out.println("loan persisted: id=" + loan.getId());
        return loan;
    }

    @Override
    public Loan saveLoan(Loan loan) {
        em.merge(loan);
        return loan;
    }

    @Override
    public Long incrementLoan(Integer loanId, long amount) {
        Loan loan = em.find(Loan.class, loanId);
        if (loan != null) {
            long sum = loan.getSum() + amount;
            /*
             * The entity is bound to the entity manager,
             * because it was returned by the find method.
             * We can simply set its properties and
             * the entity manager will update the datasource
             * after the method returns and the transaction commits. 
             * No need to call persist or merge.
             */
            loan.setSum(sum);
            return sum;
        }
        return null;
    }

    @Override
    public boolean deleteLoan(Integer loanId) {
        Loan loan = em.find(Loan.class, loanId);
        if (loan != null) {
            em.remove(loan);
            return true;
        }
        return false;
    }

    @Override
    public Loan findLoan(Integer loanId) {
        return em.find(Loan.class, loanId);
    }

    @Override
    public List<Loan> requestLoans(LoanRequest loanRequest) {
        User user;
        TypedQuery<User> query = em.createQuery("select user from User user where user.firstName = :firstName and user.lastName = :lastName", User.class);
        query.setParameter("firstName", loanRequest.getFirstName());
        query.setParameter("lastName", loanRequest.getLastName());
        List<User> users = query.getResultList();
        if (users.isEmpty()) {
            user = new User();
            user.setFirstName(loanRequest.getFirstName());
            user.setLastName(loanRequest.getLastName());
            //new entities must be persisted
            em.persist(user);
        } else {
            user = users.get(0);
        }

        List<Loan> loans = new ArrayList<>();
        Long[] totals = loanRequest.getTotals();
        for (int i = 0; i < totals.length; i++) {
            Loan loan = new Loan();
            loan.setSum(totals[i]);
            loan.setUser(user);
            em.persist(loan);
            loans.add(loan);
        }

        return loans;
    }
}

單元測試:

@Test
public void testLoan() {
    User user = loanService.createUser(newUser());

    Loan loan1 = new Loan();
    loan1.setSum(10L);
    loan1.setUser(user);

    Loan loan2 = loanService.createLoan(loan1);
    assertNotNull(loan2);

    Integer loanId = loan2.getId();

    assertNotNull(loanId);
    assertEquals(loan1.getSum(), loan2.getSum());
    assertEquals(loan1.getUser(), user);

    User user2 = loanService.createUser(newUser());
    loan2.setUser(user2);
    loan2.setSum(20L);
    Loan loan3 = loanService.saveLoan(loan2);
    assertLoanEquals(loan2, loan3);

    Long total = loanService.incrementLoan(loanId, 10L);
    assertNotNull(total);
    assertEquals((Long)(loan3.getSum() + 10L), total);

    loan3.setSum(total);

    Loan loan4 = loanService.findLoan(loanId);
    assertLoanEquals(loan3, loan4);

    boolean result = loanService.deleteLoan(loanId);
    assertTrue(result);

    Loan loan5 = loanService.findLoan(loanId);
    assertNull(loan5);

    Long[] totals = new Long[]{1L,2L,3L};
    LoanRequest loanRequest = new LoanRequest();
    loanRequest.setFirstName("Amerigo");
    loanRequest.setLastName("Vespucci");
    loanRequest.setTotals(totals);

    List<Loan> loans = loanService.requestLoans(loanRequest);
    assertNotNull(loans);
    assertEquals(3, loans.size());
    for (int i = 0; i < 3; i++) {
        assertEquals(totals[i], loans.get(i).getSum());
        loanService.deleteLoan(loans.get(i).getId());
    }


}

void assertLoanEquals(Loan loan1, Loan loan2) {
    assertNotNull(loan1);
    assertNotNull(loan2);
    assertEquals(loan1.getSum(), loan2.getSum());
    assertUserEquals(loan1.getUser(), loan2.getUser());
    assertEquals(loan1.getId(), loan2.getId());
}
void assertUserEquals(User user, User user2) {
    assertNotNull(user);
    assertNotNull(user2);
    assertEquals(user.getId(), user2.getId());
    assertEquals(user.getFirstName(), user2.getFirstName());
    assertEquals(user.getLastName(), user2.getLastName());
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM