Hibernate does not flush persistence context before selection

hibernate flush
hibernate flush mode
entitymanager flush
hibernate auto flush
jpa flush mode
spring data jpa flush mode
hibernate disable auto flush
jpa repository flush

I am trying to user hibernate with spring transaction support.I've configured session factory as bean(see below) and made a DAO class.I am also using spring test support -- running the test class with @Transactional annotation,which(as i understand) should allow me to call multiple dao operations in a single transaction.I read that hibernate should flush its persistence context either after commit or before a select is executed.I had my hopes on the latter.But most of my tests failed because(according to the logs) hibernate didn't flush it's persistence context after insert and before select.

Here is the spring part of the spring configuration:

@Configuration
@PropertySource("/db.properties")
@EnableTransactionManagement
@ComponentScan(basePackages = "repository")
public class DataAccessConfiguration {

 @Bean
    public SessionFactory sessionFactory() throws IOException {
        LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
        sessionFactoryBean.setDataSource(pool());
        sessionFactoryBean.setPackagesToScan("domain");
        sessionFactoryBean.setHibernateProperties(hibernateProperties());
        sessionFactoryBean.afterPropertiesSet();
        return sessionFactoryBean.getObject();
    }

    private Properties hibernateProperties() {
        Properties hibernateProp = new Properties();
        hibernateProp.put("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect");
        hibernateProp.put("hibernate.format_sql", true);
        hibernateProp.put("hibernate.use_sql_comments", true);
        hibernateProp.put("hibernate.show_sql", true);
        hibernateProp.put("hibernate.max_fetch_depth", 3);
        hibernateProp.put("hibernate.jdbc.batch_size", 10);
        hibernateProp.put("hibernate.jdbc.fetch_size", 50);
        hibernateProp.put("hibernate.hbm2ddl.auto", "update");
        return hibernateProp;
    }

    @Bean
    @Profile("hibernate")
    public PlatformTransactionManager transactionManager() throws IOException {
        HibernateTransactionManager manager = new HibernateTransactionManager(sessionFactory());
        manager.setDataSource(pool());

        return manager;
    }

    @Bean
    @Profile("jdbc")
    PlatformTransactionManager jdbcTransactionManager(){
        DataSourceTransactionManager manager = new DataSourceTransactionManager();
        manager.setDataSource(pool());

        return manager;
    }
}

I am using two platform transaction managers -- one for jdbc and another for hibernate.I've tried deleting the jdbc manager leaving only hibernate(didn't work).

Here is the dao class:

@Repository
@Profile("hibernate")
public class UserDaoHibernate implements UserDao {

    private final SessionFactory sessionFactory;

    public UserDaoHibernate(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    @Override
    public void insert(User user) {
        sessionFactory.getCurrentSession().save(user);
    }

    @Override
    public void addRole(User user, Role role) {
        NativeQuery addRoleQuery = sessionFactory.getCurrentSession()
                .createNativeQuery("INSERT INTO roles(user_name, user_role) VALUES(:user_name,:user_role)");
        addRoleQuery.setParameter("user_name",user.getUsername());
        addRoleQuery.setParameter("user_role", role.toString().toUpperCase());
        addRoleQuery.executeUpdate();
    }

    @Override
    public boolean hasRole(User user, Role role) {
        NativeQuery hasRoleQuery = sessionFactory.getCurrentSession()
                .createSQLQuery("SELECT * FROM roles WHERE user_name = :user_name AND user_role = :user_role");
        hasRoleQuery.setParameter("user_name", user.getUsername());
        hasRoleQuery.setParameter("user_role", role.toString().toUpperCase());
        return hasRoleQuery.getResultList().size() == 1;
    }

}

The test code that fails with the test class is here :

@ContextConfiguration(classes = {DataAccessConfiguration.class})
@Rollback
@ExtendWith(SpringExtension.class)
@ActiveProfiles("hibernate")
@Transactional
public class UserDaoTest {

    @Autowired
    private UserDao sut;

    private User user = new User("NEW_USERNAME123", "NEW_PASSWORD123", LocalDate.now().minusYears(20),
            "FIRST_NAME", "LAST_NAME");

    @Test
    public void shouldAddNewRoleToUser(){
        sut.insert(user);
        assertFalse(sut.hasRole(user,Role.MEMBER));

        sut.addRole(user, Role.MEMBER);

        assertTrue(sut.hasRole(user,Role.MEMBER));
    }

}

And here is the output i get afte running the test method(transaction comments not included):

Hibernate: 
    select
        nextval ('USER_ID_SEQUENCE')
Hibernate: 
    /* dynamic native SQL query */ SELECT
        * 
    FROM
        roles 
    WHERE
        user_name = ? 
        AND user_role = ?
Hibernate: 
    /* dynamic native SQL query */ INSERT 
    INTO
        roles
        (user_name, user_role) 
    VALUES
        (?,?)
Nov 13, 2019 3:05:31 AM org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions
WARN: SQL Error: 0, SQLState: 23503
Nov 13, 2019 3:05:31 AM org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions
ERROR: ERROR: insert or update on table "roles" violates foreign key constraint "fk_roles_user_id"
  Detail: Key (user_name)=(NEW_USERNAME123) is not present in table "users_table".

When i programmatically commit the session with sessionFactory.getCurrentSession().getTransaction().commit(); at end of insert everything works.

Try to make insert method @Transactional or use sessionFactory.getCurrentSession().flush()

Flushing, The EntityManager and the Hibernate Session expose a set of methods, before executing any native SQL query that has no registered synchronization SELECT a.id AS id1_0_ , a.title AS title2_0_ FROM Advertisement a� You're right, it could detect that some unsave changes have to be flushed to the database, and then flush the hibernate session only when required. I didn't understand your question. It would be interesting to know if the flush happens before the select statement is issued, or after (you could set show_sql paramater for example).

You should make "shouldAddNewRoleToUser()" method annotated with @Transactional and also check have you configured spring session context in application for transactions.

Hibernate does not flush persistence context before selection, Try to make insert method @Transactional or use sessionFactory. getCurrentSession().flush(). In other words, JTA transactions and persistence context propagation are not supported in J2SE (you will have to propagate the persistence context yourself, e.g. using the thread local session pattern popular in the Hibernate community).

So far the only thing i found about my issue is here How does AUTO flush strategy works

Apparently the AUTO flush mode doesn't guarantee flush before EVERY query(At least not with hibernate own API).I am not 100% sure if this is my case,i'll try to find out.

The Dark Side of Hibernate Auto Flush, The ALWAYS mode is going to flush the persistence context before any query execution (HQL or SQL). This time, Hibernate applies no optimization and all pending entity state transitions are going to be synchronized with the current database transaction. The following are top voted examples for showing how to use org.hibernate.FlushMode.These examples are extracted from open source projects. You can vote up the examples you like and your votes will be used in our system to generate more good examples.

JPA + Hibernate - Flushing Persistence Context, By invoking EntityManager#flush() method we can synchronize the current That means flush() will not make current changes visible to other EntityManager instances or An automatic flush can be invoked before transaction commit. createQuery("Select e from Employee e where e.department ='IT'");� The non-transactional flush concept does not exist in JDO, in contrast to Hibernate. For this reason, you need to set up the chosen JDO implementation for a specific environment. Specifically, you need to set it up explicitly for JTA synchronization, to detect an active JTA transaction itself.

5 Common Hibernate Mistakes That Cause Dozens of Unexpected , 19:18:57,148 DEBUG [org.hibernate.SQL] -. select. author0_.id as id1_0_, You might not even recognize it because Hibernate and your database process to call the flush() and clear() methods on the EntityManager before you execute the � A newly created entity which is not mapped to any database row is considered to be in the New or Transient state. Once it becomes managed, the Persistence Context issues an insert statement at flush time. Managed (Persistent) A Persistent entity is associated with a database row, and it is being managed by the currently running Persistence Context.

10 Common Hibernate Mistakes That Cripple Your Performance, So, it probably doesn't take much to improve your application. This problem occurs when Hibernate performs 1 query to select n entities and then to call the flush and clear methods on your EntityManager before you execute a bulk update . When nullable is disabled this method may create a new proxy or return an existing proxy; if it does not exist, throw an exception. When nullable is enabled, the method does not create new proxies (but might return an existing proxy); if it does not exist, return null. When eager is enabled, the object is eagerly fetched

Comments
  • @Transactional is on the Test Class which,according to documentation,should make all methods also transactional.
  • Annotating a test method with @Transactional causes the test to be run within a transaction that will, by default, be automatically rolled back after completion of the test. If a test class is annotated with @Transactional, each test method within that class hierarchy will be run within a transaction. Test methods that are not annotated with @Transactional (at the class or method level) will not be run within a transaction. Furthermore, tests that are annotated with @Transactional but have the propagation type set to NOT_SUPPORTED will not be run within a transaction.
  • By default, test transactions will be automatically rolled back after completion of the test; however, transactional commit and rollback behavior can be configured declaratively via the @Commit and @Rollback annotations at the class level and at the method level. You can read the spring documentation here [docs.spring.io/spring/docs/current/javadoc-api/org/…
  • I did add it,even though i don't clearly understand why do i need that,it didn't do anything.Can you explain why do you think i need @Transactional on a test method?@Transactional is on the class method which means "At the class level, this annotation applies as a default to all methods of the declaring class and its subclasses.".I know that it works because i run the same test with spring jdbc.The problem here is not in transaction,or at least i don't see it there,but in that hibernate,for some reason that i don't understand,doesn't flush its context before selection.
  • Flushing context programmatically wokrs,but it should do it by deault.