插入后如何防止JPA回滚异常?

How to prevent JPA rollback exception after insertion?

电子贺卡:

@Entity
@Table (name = "ecard")
public class Ecard {
    @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
    private int id;

    @OneToOne (cascade = CascadeType.ALL)
    @JoinColumn(name = "IdUser", nullable = false)
    private User user;

    @ManyToOne 
    @JoinColumn(name = "IdBank", nullable = false)
    private Bank bank;

    @Column (name = "accountNumber", nullable = false)
    private int accountNumber;

    public ECard(User user, Bank bank, int accNumber) {
       this.user = user;
       this.bank = bank;
       this.accountNumber = accNumber;
    }
}

用户:

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

        @OneToOne (mappedBy = "user")
        private ECard eCard;

        @ManyToOne
        @JoinTable(
            name = "ecard", 
            inverseJoinColumns = @JoinColumn(name = "IdBank", referencedColumnName = "Id"),
            joinColumns = @JoinColumn(name = "IdUser", referencedColumnName = "Id")
        )
        private Bank bank;

public void seteCard(ECard eCard) {
        this.eCard = eCard;
    }

    public void setBank(Bank bank) {
        this.bank = bank;
    }
}

银行:

    @Entity
    @Table (name = "bank")
    public class Bank {
        @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
        private int id;

        @Column(name = "name", nullable = false, unique = true)
        private String name;
}

我已经初始化了银行 table,我想插入新用户,新电子卡链接到数据库中已经存在的银行

我是新手,所以我不明白为什么

我的代码不起作用:

        //I checked and variable bank is okay, it's method argument
        eManager.getTransaction().begin();
        User user = new User();
        ECard eCard = new ECard(user, bank, 1000);

        user.seteCard(eCard);
        user.setBank(bank); // PROBLEM
        eManager.persist(eCard);

        eManager.getTransaction().commit();

我收到回滚异常:

    Caused by: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.5.0.v20130507-3faac2b): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLException: Field 'accountNumber' doesn't have a default value
Error Code: 1364
Call: INSERT INTO ecard (IdBank, IdUser) VALUES (?, ?)
    bind => [2 parameters bound]
Query: DataModifyQuery(name="bank" sql="INSERT INTO ecard (IdBank, IdUser) VALUES (?, ?)")
    at org.eclipse.persistence.exceptions.DatabaseException.sqlException(DatabaseException.java:331)
    at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:895)
    at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeNoSelect(DatabaseAccessor.java:957)
    at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.basicExecuteCall(DatabaseAccessor.java:630)
    at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeCall(DatabaseAccessor.java:558)
    at org.eclipse.persistence.internal.sessions.AbstractSession.basicExecuteCall(AbstractSession.java:1995)
    at org.eclipse.persistence.sessions.server.ClientSession.executeCall(ClientSession.java:296)
    at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:242)
    at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:228)
    at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeNoSelectCall(DatasourceCallQueryMechanism.java:271)
    at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeNoSelect(DatasourceCallQueryMechanism.java:251)
    at org.eclipse.persistence.internal.queries.StatementQueryMechanism.executeNoSelect(StatementQueryMechanism.java:118)
    at org.eclipse.persistence.queries.DataModifyQuery.executeDatabaseQuery(DataModifyQuery.java:85)
    at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:899)
    at org.eclipse.persistence.queries.DatabaseQuery.executeInUnitOfWork(DatabaseQuery.java:798)
    at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2894)
    at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1797)
    at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1779)
    at org.eclipse.persistence.mappings.OneToOneMapping.performDataModificationEvent(OneToOneMapping.java:2181)
    at org.eclipse.persistence.internal.sessions.CommitManager.commitAllObjectsWithChangeSet(CommitManager.java:159)
    at org.eclipse.persistence.internal.sessions.AbstractSession.writeAllObjectsWithChangeSet(AbstractSession.java:4200)
    at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabase(UnitOfWorkImpl.java:1439)
    at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabaseWithChangeSet(UnitOfWorkImpl.java:1529)
    at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.commitRootUnitOfWork(RepeatableWriteUnitOfWork.java:277)
    at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitAndResume(UnitOfWorkImpl.java:1167)
    at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commit(EntityTransactionImpl.java:132)
    ... 10 more
Caused by: java.sql.SQLException: Field 'accountNumber' doesn't have a default value
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1074)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4120)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4052)
    at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2503)
    at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2664)
    at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2794)
    at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2155)
    at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2458)
    at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2375)
    at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2359)
    at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:885)
    ... 34 more
Java Result: 1

当我删除第 user.setBank(bank) 行时效果很好,但我需要参考用户中的银行,所以删除这一行对我来说不太好。

当我为 accountNumber 设置默认值时,我在 eCard table 中得到两行,一个常规行和第二行具有默认 accountNumber 和相同的 IdUser 和 IdBank

如何避免该异常以及如何在 Bank 上设置引用?

您已经通过添加此

声明ecard作为您的映射table
@ManyToOne
    @JoinTable(
        name = "ecard", 
        inverseJoinColumns = @JoinColumn(name = "IdBank", referencedColumnName = "Id"),
        joinColumns = @JoinColumn(name = "IdUser", referencedColumnName = "Id")
    )

但是ecard也作为一个实体,通过关联bankId和userId加上一个accountNumber字段来映射用户和银行的信息。 这就是 Jpa 在用户持久化中添加 2 条记录的原因 - 一条用于用户 class name = 'ecard' 映射中定义的映射,一条用于 ECard eCard = new ECard(user, bank, 1000);

我建议您将 accountNumber 字段移动到银行 class 并删除 ecard class 因为 JoinColumn (name = 'ecard'... 中的映射已经假定存在 table ecard 只有 2 列 userId 和 bankId,并且会以默认方式处理它。

或者删除用户 class 中的银行字段,因为用户已经引用了 ecard,后者引用了 bank。但是现在您必须管理银行和用户手动就像你在上面的代码中所做的那样。