JPA更新一对一关联对象导致重复条目异常。

3
我正在使用JSF 2.0、Tomcat 7.x和EclipseLink作为JPA提供程序。我想更新一个实体实例,其中包含一个一对一的关联,更准确地说是Booking属性中的User属性名为booker。不幸的是,如果我调用我的updateBooking() API方法,EclipseLink会创建一个新的booker而不是只使用已经存在的用户。以下是Eclipse控制台的堆栈跟踪和相关代码片段 - 感谢您提供一些提示!
堆栈跟踪:
Caused by: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.3.1.v20111018-r10243): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '1' for key 'EMAIL'
Error Code: 1062
Call: INSERT INTO user (EMAIL, NAME, PASSWORD) VALUES (?, ?, ?)
bind => [3 parameters bound]
Query: WriteObjectQuery(LogString User - id: 1; name: 1; email: 1;)
at org.eclipse.persistence.exceptions.DatabaseException.sqlException(DatabaseException.java:324)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:840)
...
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '1' for key 'EMAIL'
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)

DevelopmentController.java

public String testUpdateBooking(){
    String str = "/development.xhtml?faces-redirect=true";
    try {
        Integer bookingId = 22;
        Integer bookerId = 1;
        BookerApi api = new BookerApi();
        Booking booking = api.readBooking(bookingId);
        User booker = api.readUser(bookerId);
        booking.setBooker(booker);
        api.updateBooking(booking);
        message = "DevelopmentController.testUpdateBooking() " + booking;
    } catch (RuntimeException exc){
        message = "EXCEPTION DevelopmentController.testUpdateBooking();"; 
        exc.printStackTrace();
    }
    return str;
}

BookerApi.java

public void updateBooking(Booking booking){
    emf = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);
    EntityManager em = emf.createEntityManager();
    em.getTransaction().begin();
    Booking persBooking = em.find(Booking.class, booking.getId());
    persBooking.setCourtNo(booking.getCourtNo());
    persBooking.setStart(booking.getStart());
    persBooking.setEnd(booking.getEnd());
    persBooking.setBooker(booking.getBooker());
    em.getTransaction().commit(); // haendelt update implizit, kracht
    em.clear();
    emf.close();
}

实体预订者属性:

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Integer id; 
@Temporal(TemporalType.DATE)
@Column(nullable = false)
Date start;
@Temporal(TemporalType.DATE)
@Column(nullable = false)
Date end;   
@Column(nullable = false)
Integer courtNo;
@OneToOne(fetch = FetchType.EAGER) // Mueller S. 105, 109
@JoinColumn(name = "user")
User booker;

实体用户属性:

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id; 
@Column(unique = true, nullable = false)
private String name;
@Column(nullable = false)
private String password; // TODO verschluesseln 
@Column(unique = true, nullable = false)
private String email;

1
预订者来自哪里?实体的代码是什么? - JB Nizet
谢谢JB Nizet,我添加了相关部分! - Jochen
1个回答

0
很多事情:
1:修复
在jpa中,您不需要编写任何CRUD方法,EntityManager提供了所有所需的方法
public void updateBooking(Booking booking){
emf = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
em.merge(booking);
em.getTransaction().commit();
em.close();
emf.close();

但是

你不应该这样处理事务,事务应该在服务层打开,而不是在数据访问层打开。实际上,如果您有容器的话(使用Spring或Java EE服务器),应该使用@transactional注释通过容器来管理它。 如果您一定要手动管理,请确保在遇到异常时正确处理并回滚。EntityManager和底层事务通常具有相同的范围/生命周期。

此外,您应该为整个应用程序仅拥有一个EntityManagerFactory。

否则,在此处不需要指定获取类型,oneToOne关联始终是立即获取的。


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