JPA Оптимистичная блокировка исключения в одной теме - PullRequest
0 голосов
/ 23 ноября 2018

Я пытаюсь обновить базовую метку времени удаления, используя класс сохраняемости JPA.Вот код:

    public void delete(Document document, EntityManager em, SessionContext ctx)
                throws MyException {
            try {
                ctx.getUserTransaction().begin();
                DocumentDB documentDB = em.find(WDSDocument.class, document.getId());
                if (documentDB != null) {
                    em.lock(documentDB, LockModeType.WRITE);
                    documentDB.setDeletedAt(new Timestamp(System.currentTimeMillis()));
                    em.merge(documentDB);
                    em.flush(); // OptimisticLockException raised here
                }
                ctx.getUserTransaction().commit();
                if (log.isDebugEnabled()) log.debug("DELETED " + document.getId());
            } catch (Exception e) {
                ctx.getUserTransaction().rollback();
                throw new MyException(Utils.getError("ERR9", new String[] { "" + document.getId(), e.getMessage() }),
                        e);
            }
    }

Документ с тем же Id, что и document уже хранится в БД, и я хотел бы обновить одно поле.

* em.flush() строка вызывает исключение, как если бы другой пользователь (поток) пытался обновить этот же объект, но в моем приложении это не так.

Я читал о OptimisticLockException в JPA иЯ понимаю, что это может быть вызвано, когда один и тот же пользователь пытается обновить объект два раза подряд без предварительной очистки / фиксации в БД.

Видимо, эта ошибка возникает всякий раз, когда я пытаюсь обновить удаление documentDBвременная метка для любого объекта, поэтому я предполагаю, что в моем методе delete(), который дважды обновляет сам объект, должно быть что-то не так.До сих пор я не мог правильно ее устранить.

EDITED

Вот мой DocumentDB класс

@Entity
@Table(name = "DOCUMENTS", schema = "WP")
public class DocumentDB extends AbstractEntity {
    private static final long serialVersionUID = -98765134L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ID_DOCUMENT", nullable = false)
    private int id;

    @Column(name = "SOURCE")
    private String source = null;

    @Column(name = "CREATION_DATE")
    private Date creationDate;

    @Column(name = "TITLE")
    private String title = null;

    @Column(name = "AUTHOR")
    private String author = null;

    @Column(name = "URL")
    private String url = null;

    @Column(name = "DELETED_AT")
    private Timestamp deletedAt = null;

    @Override
    public Integer getId() {
        return id;
    }

    @Override
    public void setId(int id) {
        this.id = id;
    }


    public Date getCreationDate() {
        return creationDate;
    }

    public void setCreationDate(Date creationDate) {
        this.creationDate = creationDate;
    }

    public String getSource() {
        return source;
    }

    public void setSource(String source) {
        this.source = source;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public Timestamp getDeletedAt() {
        return deletedAt;
    }

    public void setDeletedAt(Timestamp deletedAt) {
        this.deletedAt = deletedAt;
    }
}

Хотя его абстрактный суперкласс:

@MappedSuperclass
public abstract class AbstractEntity implements Serializable {
    private static final long serialVersionUID = -98765134L;

    public abstract Integer getId();
    public abstract void setId(int id);

    /**
     * Creator of the record
     */
    @Column(name = "USER_CREATION", nullable = false)
    protected String userCreation;

    /**
     * Timestamp of creation of the record
     */
    @Column(name = "DATE_CREATION", nullable = false)
    protected Timestamp dateCreation;

    /**
     * User of the last change of the record
     */
    @Column(name = "USER_LAST_CHANGE", nullable = false)
    protected String userLastChange;

    /**
     * Timestamp of the last change of the record
     */
    @Column(name = "DATE_LAST_CHANGE", nullable = false)
    protected Timestamp dateLastChange;

    /**
     * Progressive of the variation of the record: 
     * used in optimistic locking of entity manager 
     * to avoid conflicts in insert/update
     */
    @Version
    @Column(name = "PG_VER_REC", nullable = false)
    protected int progressiveVariationRecord;

    public String getUserCreation() {
        return userCreation;
    }
    public void setUserCreation(String userCreation) {
        this.userCreation = userCreation;
    }
    public Timestamp getDateCreation() {
        return dateCreation;
    }
    public void setDateCreation(Timestamp dateCreation) {
        this.dateCreation = dateCreation;
    }
    public String getUserLastChange() {
        return userLastChange;
    }
    public void setUserLastChange(String userLastChange) {
        this.userLastChange = userLastChange;
    }
    public Timestamp getDateLastChange() {
        return dateLastChange;
    }
    public void setDateLastChange(Timestamp dateLastChange) {
        this.dateLastChange = dateLastChange;
    }
    public int getProgressiveVariationRecord() {
        return progressiveVariationRecord;
    }
    public void setProgressiveVariationRecord(int progressiveVariationRecord) {
        this.progressiveVariationRecord = progressiveVariationRecord;
    }
}

Не могли бы вы дать некоторые рекомендации относительно следующих шагов, которые необходимо выполнить, чтобы лучше понять эту проблему?

ОБНОВЛЕНИЕ

Я до сих пор не нашел основную причину проблемы,Я подозреваю, что моя реализация EclipseLink JPA делает странные вещи при обновлении сущностей внутри цикла for.

К сожалению, на данный момент у меня нет времени и ресурсов, чтобы копать глубже, и я использую пессимистическую блокировку в качестве обходного пути.Надеюсь найти реальную проблему когда-нибудь в будущем.

...