Hibernate 5.x and Spring Data 2.x : how to make th

2019-08-22 02:04发布

问题:

I am upgrading a web application from:

Spring (MVC) 4.2.9.RELEASE, Hibernate 4.3.8.Final, and 1.7.1.RELEASE

to

Spring (MVC) 5.0.2.RELEASE, Hibernate 5.2.12.Final, and Spring Data 2.0.2.RELEASE.

The web application runs on Windows and MS SQL Server 2014 Enterprise.

The upgrade did not force me to change the settings for Hibernate and JPA. However, now the program behaves very differently, which is explained below.

The following is a typcial service method in the application, which saves a new Account object, and then updates its value if it is a new object, then return it. The Account object has an ID field defined as follows:

@Id
@GeneratedValue( strategy = GenerationType.IDENTITY )
private Long id;

This is the servie method:

@Override
@Transactional
public Account saveAccount(Account acc) {

    //Step 1
    //save method from Spring Data
    accountRepository.save(acc); 

    //stringId is set manually only once and it is set only when a new accout object is created
    if (acc.getStringId() == null) { 

        //Step 2
        acc.setStringId("some_string_id");
    }

    return acc;
}

Here is the behavior BEFORE the upgrade:

Step 1: acc is the same object after the save. Its id field is updated from null to a Long value after the save.

Step 2: The new value is automatically serialized to database. Note that there is no explicit database call to the save method.

Here is the behavior AFTER the upgrade:

Step 1: Using accountRepository.save(acc) only does not update the acc object. To get the object with new id, I have to do it the following way:

acc = accountRepository.save(acc)

Step 2: the string id is not saved for a new object.

I am looking for ways to make the system work the way before it was upgrade. The reason is that the application is not trivial, and I have followed the programming pattern (good or bad) throught the application. I would like to avoid lots of changes and re-tests.

Here is the related configuration

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:jpa="http://www.springframework.org/schema/data/jpa"
       xmlns:jdbc="http://www.springframework.org/schema/jdbc"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
          http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
          http://www.springframework.org/schema/context
          http://www.springframework.org/schema/context/spring-context-3.2.xsd
          http://www.springframework.org/schema/data/jpa    
          http://www.springframework.org/schema/data/jpa/spring-jpa-1.1.xsd                              
          http://www.springframework.org/schema/tx
          http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
          http://www.springframework.org/schema/jdbc 
          http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd">

    <bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
        <constructor-arg ref="hikariConfig" />
    </bean> 

    <bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
        <property name="poolName" value="derek6HikariCP" />
        <property name="connectionTestQuery" value="${jdbc.connectionTestQuery}" />
        <property name="dataSourceClassName" value="${jdbc.dataSourceClassName}" />
        <property name="maximumPoolSize" value="${jdbc.maximumPoolSize}" />
        <property name="minimumIdle" value="${jdbc.minimumIdle}" />
        <property name="idleTimeout" value="${jdbc.idleTimeout}" />
        <property name="connectionTimeout" value="${jdbc.connectionTimeout}" />
        <property name="dataSourceProperties">
            <props>
                <prop key="url">${jdbc.url}</prop>
                <prop key="user">${jdbc.username}</prop>
                <prop key="password">${jdbc.password}</prop>
            </props>
        </property>     
    </bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <constructor-arg ref="dataSource"/>
    </bean>

    <bean  id="entityManagerFactory" name="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
        </property>
        <property name="packagesToScan" value="myproject.entity" />
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.dialect">${hibernate.dialect}</prop>
                <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
                <prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
                <prop key="hibernate.max_fetch_depth">${hibernate.max_fetch_depth}</prop>                                 
                <prop key="hibernate.jdbc.fetch_size">${hibernate.jdbc.fetch_size}</prop>
                <prop key="hibernate.jdbc.batch_size">${hibernate.jdbc.batch_size}</prop>
            </props>
        </property>
    </bean>     

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="emf" />
    </bean>

    <tx:annotation-driven transaction-manager="transactionManager"/>   

    <context:annotation-config />  

    <jpa:repositories base-package="myproject.entity"
        entity-manager-factory-ref="emf" transaction-manager-ref="transactionManager" />  

</beans>

回答1:

I guess that is due to the flush strategy. You should look into : https://vladmihalcea.com/how-does-the-auto-flush-work-in-jpa-and-hibernate/. to understand better.

Between this 2 version changes, it might happen that they have changed their flush strategy default. so first read the above document & configure your code as you want.