Search code examples
javahibernatejpaentitypersistence

How to prevent JPA rollback exception after insertion?


ECard:

@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;
    }
}

User:

     @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;
    }
}

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;
}

I have already initialized Bank table and I would like to insert new User with new ECard linked to Bank that already exists in database

I'm new to this whole thing so I can't understand why

My code that won't work:

        //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();

I get a rollback Exception:

    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

When i delete line user.setBank(bank) it works great, but I need reference on Bank in User, so deleting this line isn't so good for me.

When I put default value for accountNumber, I get two rows in eCard table, one regular row and second row with default accountNumber and same IdUser and IdBank

How to avoid that exception and how to set reference on Bank??


Solution

  • You are already declaring ecard as your mapping table by adding this

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

    But ecard is also used as an entity to map information of user and bank by associating bankId and userId plus an accountNumber field. That's why Jpa is adding 2 record in persist of user - one for defined mapping in user class name = 'ecard' mapping and one for the ECard eCard = new ECard(user, bank, 1000);

    I would suggest you move accountNumber field to bank class and delete ecard class because mapping in JoinColumn (name = 'ecard'... already assumes there exists a table ecard with just 2 columns userId and bankId and would handle it in default way.

    Or remove the bank field in user class because user already has reference to ecard which has reference to bank.But now you will have to manage the association between bank and user manually like you are doing it in above code.