JPA: persist() молча, без исключений не вносит изменения в БД Postgres, но прибавляет счетчик ID

Не срабатывает запись persist() для тех же объектов, которые нормально читаются, удаляются и обновляются посредством JPA. В базе Postgres есть таблица students:

CREATE TABLE students
(
    person_id SERIAL PRIMARY KEY,
    group_id INTEGER REFERENCES groupz (group_id),
    student_year INTEGER,
    person_name CHARACTER VARYING(30)
);

Класс Student наследует ID-поле у родителя Person:

@MappedSuperclass
public abstract class Person {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    protected int person_id;
    
    public int getId() {
        return person_id;
    }
    ...

У Person также есть дочерний класс Professor, он нормально пишется с persist(). Метод из DAO-класса:

    public boolean create(Student student) throws DAOException {
        boolean result = false;
        try {
            EntityManager em = emf.getFactory().createEntityManager();
            em.getTransaction().begin();
            em.persist(student);
            em.flush();
            em.close();
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new DAOException("Could not create Student", e);
        }
        return result;
    }

Странно, что нет exceptions в логах, консоли и т.п. SQL-запрос отражается в консоли - он валиден, точно такой же запрос в pgAdmin дает нужный результат. При этом, когда я делаю это через pgAdmin, новая запись приходит с намного большим ID - сообразно количеству неудачных попыток сделать запись при помощи JPA/Hibernate.


Ответы (1 шт):

Автор решения: kami

Описанное поведение (ID успешно зарезервированы, но ни одной записи с ними не видно) явно указывает на незавершенные (открытые) транзакции.
Postgres получил данные, выделил под них ресурсы, но держит в заблокированном состоянии до тех пор, пока транзакции не будут закрыты (commit). Теоретически - увидеть данные можно, используя "грязное чтение" (read uncommited), но скорее всего по истечению времени транзакции будут автоматически закрыты и данные пропадут.

Верное решение здесь - в явном виде завершить транзакцию, которая в рамках единственного действия, по большому счету, и не нужна. В итоге, должно получиться что-то вроде:

    public boolean create(Student student) throws DAOException {
        boolean result = false;
        EntityManager em = emf.getFactory().createEntityManager();
        EntityTransaction trn = em.getTransaction();
        try {
            trn.begin();
            em.persist(student);
            em.flush();
            em.close();
            trn.commit();
        } catch (Exception e) {
            trn.rollback();
            logger.error(e.getMessage(), e);
            throw new DAOException("Could not create Student", e);
        }
        return result;
    }
→ Ссылка