简体   繁体   中英

Hibernate Constraint Violation when persisting associated entities

I'm developing a Java Spring Web Application for an Online Movie Ticket Service.

A user has a designated amount of points and is allowed to use these points to pay for a part of their tickets. If the user decides to cancel a transaction his points are reimbursed. These point movements are called PointTransactions.

Transactions are stored in a transactions table and PointTransactions are stored in a pointTransactions table with a reference to the parent transaction.

I am having trouble when persisting a PointTransaction Entity that references a new Transaction Entity on the same method.

Here is the code I am currently using:

@Override
@Transactional
public TransactionDTO confirmCheckout(...) {

    /**Validations removed and parameters simplified to improve readability**/

    Transaction transaction = transactionDao.create(...);
    pointsTransactionDao.create(transaction, ...);

    return new TransactionDTO(transaction);
}

The DAO functions have been duely tested and work independently, however when the above code is run I receive the following exception.

javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: could not execute statement
at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:347)
at ar.edu.itba.paw2018b.persistence.PointTransactionsHibernateDaoImpl.create(PointTransactionsHibernateDaoImpl.java:32)

Caused by: org.postgresql.util.PSQLException: ERROR: insert or update on table "pointtransactions" violates foreign key constraint "pointtransaction_transaction_transid_fk"
Detail: Key (transaction)=(23) is not present in table "transactions".

When viewing the code during runtime I can confirm that the Transaction with ID = 23 is the one created in the confirmCheckout() method. It appears the constraint is violated because the Transaction record is not commited to the DB before the PointTransaction record.

This error began once I changed the PersistenceContext of my EntityManagers from the default to the ExtendedType , the change was pushed by my need to Lazily initialize most of my references. I believe the problem is strongly related to this however I do not have enough of a grasp on Hibernate to infer the best way to tackle the issue.

Below is the code of my Daos and Models.

Thank you for your time.

@Repository
public class TransactionHibernateDaoImpl implements TransactionDao {

    @PersistenceContext(type = PersistenceContextType.EXTENDED)
    private EntityManager em;

    @Override
    public Transaction create(User userId, Screening screeningId, String seat, String food, double price, boolean paid, Timestamp date, String confirmationCode, int status) {
        final Transaction transaction = new Transaction(userId,screeningId,seat,food,price,date,paid,confirmationCode, status);
        em.persist(transaction);
        return transaction;
    }
    public void setEm(EntityManager em) {
        this.em = em;
    }
    /** Unrelated Methods **/
}

.

@Repository
public class PointTransactionsHibernateDaoImpl implements PointTransactionsDao {

    @PersistenceContext(type = PersistenceContextType.EXTENDED)
    private EntityManager em;

    @Override
    public PointTransactions create(User userid, int conversion, Transaction transaction, int price, int reason, Timestamp date)
    {
        final PointTransactions ptransaction = new PointTransactions(userid,conversion,transaction,price, reason, date);
        em.persist(ptransaction);
        return ptransaction;
    }
    public void setEm(EntityManager em) {
        this.em=em;
    }
    /** Unrelated Methods **/
}

.

@Entity
@Table(name = "pointtransactions")
public class PointTransactions {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @SequenceGenerator(sequenceName = "transactions_transid_seq", name = "transactions_transid_seq", allocationSize = 1)
    @Column(name = "pointtransid", nullable = false)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "userid")
    private User userid;

    @Column
    private int conversion;

    @Column
    private Timestamp date;

    @Column
    private int price;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "transaction")
    private Transaction transaction;

    @Column(nullable = false)
    private int reason;

    public PointTransactions(){}


    public PointTransactions(User userId, int conversion, Transaction transaction, int price, int reason, Timestamp date){
        this.userid = userId;
        this.conversion = conversion;
        this.transaction = transaction;
        this.price = price;
        this.reason = reason;
        this.date = date;
    }
    /** Getters and Setters **/
}

.

@Entity
@Table(name = "transactions")
public class Transaction {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @SequenceGenerator(sequenceName = "transactions_transid_seq", name = "transactions_transid_seq", allocationSize = 1)
    @Column(name = "transid", nullable = false)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "userid")
    private User userid;

    @Column(name = "transactiondate",nullable = false)
    private Timestamp date;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "screeningid")
    private Screening screeningid;

    @Column(nullable = false)
    private String seat;

    @Column(name = "fooddetails", length = 1024)
    private String food;

    @Column(nullable = false)
    private double price;

    @Column(nullable = false)
    private boolean paid = false;

    @Column
    private String confirmationCode;

    @Column
    private int status;

    public Transaction(){}

    public Transaction( User userId, Screening screeningId, String seat, String food, double price, Timestamp date, boolean paid, String confirmationCode, int status){
        this.userid = userId;
        this.date = date;
        this.screeningid = screeningId;
        this.seat = seat;
        this.food=food;
        this.price = price;
        this.paid = paid;
        this.confirmationCode = confirmationCode;
        this.status = status;
    }
/** Getters and Setters **/
}

you are pretty close on your observation, long story short, PerssitenceContextType.EXTENDED - means you are responsible for managing your stuff ;container will not do it for you.

"Transaction with ID = 23 is the one created in the confirmCheckout() method" - Can you please check the "ID = 23" persist in the DB ?

I guess not, means your changes are not committed in the order or they should or they are not flushed so you end up with " violates foreign key constraint "pointtransaction_transaction_transid_fk" "

Dirty solution[do the stuff yourself] -- that is commit/flush the ID=23 changes,check it persist in the DB and then proceed with rest of the operations.

Here is the long story you were looking for - https://vladmihalcea.com/initialize-lazy-proxies-collections-jpa-hibernate/ sincerely hoping for a movie ticket

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM