OptimisticLockType.NONE ignored or not used correctly?

I would like to achieve the following using Hibernate 6.1.6.Final:

  1. Have an entity with a version column.
  2. Do not use optimistic logging per default.
  3. Only use optimistic logging explicitly by passing it to EntityManager#find or specifying it for a query.

So I added a new column and the @Version annotation and the @OptimisticLocking(type = OptimisticLockType.NONE) annotation. The documentation states the following:

NONE
optimistic locking is disabled even if there is a @Version annotation present

I assumed that should do it, but it seems the OptimisticLockType.NONE is ignored. Persist initializes the version column and merge increments it.

Did I understand the whole thing wrong or is it a configuration error or a bug?

I have created a reproducer using your test example.

Here is the code:

package org.hibernate.bugs.entities;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.Version;
import org.hibernate.annotations.OptimisticLockType;
import org.hibernate.annotations.OptimisticLocking;

@Entity
@OptimisticLocking(type = OptimisticLockType.NONE)
public class Example {
    @Id
    @GeneratedValue
    private Long id;

    private String name;

    @Version
    private Long rowVersion;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Long getRowVersion() {
        return rowVersion;
    }

    public void setRowVersion(Long rowVersion) {
        this.rowVersion = rowVersion;
    }
}
package org.hibernate.bugs;

import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.Persistence;

import org.hibernate.bugs.entities.Example;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

/**
 * This template demonstrates how to develop a test case for Hibernate ORM, using the Java Persistence API.
 */
public class JPAUnitTestCase {

	private EntityManagerFactory entityManagerFactory;

	@Before
	public void init() {
		entityManagerFactory = Persistence.createEntityManagerFactory( "templatePU" );
	}

	@After
	public void destroy() {
		entityManagerFactory.close();
	}

	// Entities are auto-discovered, so just add them anywhere on class-path
	// Add your tests, using standard JUnit.
	@Test
	public void hhh123Test() throws Exception {
		EntityManager entityManager = entityManagerFactory.createEntityManager();

		// Transaction 1 - create entity
		entityManager.getTransaction().begin();

		// Create new entity
		Example example = new Example();
		example.setName("Example1");
		entityManager.persist(example);
		entityManager.flush();

		// Verify that row version is null - it is 0L
		Assert.assertNull(example.getRowVersion());

		Long exampleId = example.getId();
		entityManager.getTransaction().commit();

		// Transaction 2 - update entity
		entityManager.getTransaction().begin();
		example = entityManager.find(Example.class, exampleId);
		example.setName("Example2");
		example = entityManager.merge(example);
		entityManager.flush();

		// Verify that row version is still null - it is 1L
		Assert.assertNull(example.getRowVersion());
		entityManager.getTransaction().commit();

		entityManager.close();
	}
}

I would appreciate any insight on the matter! Thank you very much in advance!

You wrote that the version is incremented, but is it also checked in the where clause of the update statement? Optimistic locking usually refers to the actual checking that versions match the expected previous version. I think it should still increment the version so that other transactions that actually do optimistic locking will see that the data was actually changed.

It would be good if you check whether Hibernate 5.6 behaves the same way though. If Hibernate 5 behaves the same way, then it’s not a bug, but rather by design. Otherwise, please create a JIRA issue for this and attach the test case to the issue.

Thank you very much for your reply!

You wrote that the version is incremented, but is it also checked in the where clause of the update statement?

Yes it is and the StaleObjectStateException is thrown.

It would be good if you check whether Hibernate 5.6 behaves the same way though. If Hibernate 5 behaves the same way, then it’s not a bug, but rather by design. Otherwise, please create a JIRA issue for this and attach the test case to the issue.

Hibernate 5.6.14.Final behaves the same way, so I’ll accept it as it is then. Thank you!
I love your hibernate-test-case-templates, they make it really easy to test and reproduce stuff!

Yes it is and the StaleObjectStateException is thrown.

Hmm, that seems wrong to me though. Could you please create a JIRA issue for this?

Interesting. I have created a Jira issue: [HHH-16095] - Hibernate JIRA