Вопрос

I an new to use hibernate caching (first level, 2nd level and query cache).

My project is configured using Spring MVC and JPA.

I am testing first level cache using JUnit test case below.

public class FirstLevelCacheTest
{
    private static final Logger logger = LoggerFactory.getLogger(FirstLevelCacheTest.class);

    @PersistenceContext
    private EntityManager       entityManager;

    @Test
    public void testFirstLevelCacheTest()
    {
        Long clientId = 1L;

        // fetch the client entity from database first time
        Client client1 = entityManager.find(Client.class, clientId);
        logger.debug("Client Name : {}", client1.getName());

        // fetch the client entity again
        client1 = entityManager.find(Client.class, clientId);
        logger.debug("Client Name : {}", client1.getName());
    }
}

And my entity class is defined as :

@Entity
@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
@Table(name = "client")
public class Client extends BaseModel
{
    // instance variable declaration

    // getter and setter methods for instance variables
}

This should execute native query once in case first level cache is by default enabled. But I am getting result below while executing this query:

Hibernate: select client0_.id as id0_0_, client0_.created as created0_0_, client0_.createdBy as createdBy0_0_, client0_.updated as updated0_0_, client0_.updatedBy as updatedBy0_0_, client0_.contactNo as contactNo0_0_, client0_.contactPerson as contactP7_0_0_, client0_.contactPersonEmail as contactP8_0_0_, client0_.contactPersonNo as contactP9_0_0_, client0_.email as email0_0_, client0_.name as name0_0_ from client client0_ where client0_.id=?
[main] DEBUG Client Name : Client1
Hibernate: select client0_.id as id0_0_, client0_.created as created0_0_, client0_.createdBy as createdBy0_0_, client0_.updated as updated0_0_, client0_.updatedBy as updatedBy0_0_, client0_.contactNo as contactNo0_0_, client0_.contactPerson as contactP7_0_0_, client0_.contactPersonEmail as contactP8_0_0_, client0_.contactPersonNo as contactP9_0_0_, client0_.email as email0_0_, client0_.name as name0_0_ from client client0_ where client0_.id=?
[main] DEBUG Client Name : Client1

Below is my persistence related configurations:

@Configuration
@EnableTransactionManagement
public class PersistanceConfig
{
    // injection

    private String[] PACKAGES_TO_SCAN = new String[] { "com.mypackage1", "com.mypackage2" };

    @Bean
    public DataSource dataSource()
    {
        // dataSource setting 'com.mysql.jdbc.Driver' as a jdbc driver
    }

    private Properties jpaProperties()
    {
        Properties properties = new Properties();

        properties.put(AvailableSettings.DIALECT, hibernateDialect);
        properties.put(AvailableSettings.SHOW_SQL, hibernateShowSql);

        // 2nd level cache
        properties.put("hibernate.cache.use_second_level_cache", true);
        properties.put("hibernate.cache.region.factory_class", "org.hibernate.cache.ehcache.EhCacheRegionFactory");

        return properties;
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory()
    {
        // entity manager settings using dataSource and jpaProperties
    }

    @Bean
    public JpaTransactionManager transactionManager()
    {
        // transaction manager settings using entityManagerFactory
    }
}

Can anybody help me to sort out an issue or anything I am doing wrong ?

Thanks in advance !

Это было полезно?

Решение

You need to annotate the test method with @Transactional or use the Spring TransactionTemplate.

Even if retrieving an entity doesn't require a transaction, grouping multiple calls inside one would clearly specify the Persistence Context boundaries (where it is meant to start and where it should end).

To replicate the 1st Level Cache you need the same Unit Of Work throughout your test method, and annotating the test with @Transactional would start a transaction (a Spring logical transaction that's bound to the current executing Persistence Context transaction which correlates to the actual physical database transaction).

When the test method ends the Transaction is usually roll-back so no changes will be propagated to any successive test, but within the currently executing test method you get to see your own changes, which is the Isolation property in ACID.

Другие советы

I have tested you scenario in a simple command line application and could not reproduce the problem. Tested with Hibernate 4.0.1 and Hibernate 4.3. Basically I fetched the same entity twice outside a transaction and the second SQL query is not executed. IMPORTANT: I called EntityManager.find() one after another, without any other operations between them (like opening a transaction, case which will trigger another SQL select).

I recommend you the following: 1. Make sure that you look also in the logs of your DB server (to be sure that Hibernate does not log the queries, although it does not send them). 2. Upgrade Hibernate to a newer version.

Another note: as far as I know, there is no requirement that Hibernate uses such a Cache.

persistence.xml I've used:

<persistence version="2.0" xmlns="http://xmlns.jcp.org/xml/ns/persistence"  
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
        xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_0.xsd"> 

    <persistence-unit name="my-PU" transaction-type="RESOURCE_LOCAL">

                <!--<provider>org.hibernate.ejb.HibernatePersistence</provider>-->
                <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>

                <class>com.package.Entity</class>
                <exclude-unlisted-classes>false</exclude-unlisted-classes>
                <properties>
                        <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
                        <property name="hibernate.hbm2ddl.import_files" valu="sql/import-users.sql"/>
                        <property name="hibernate.show_sql" value="true"/>
                        <property name="hibernate.format_sql" value="false"/>
                </properties>
    </persistence-unit>
</persistence>

@Transactional is responsible to handle to get the session object from the sessionFactory and the entityManger object from the entityManagerFactory First Level cache happened at session level or entityManger level so @Tranaction annotation is very important here

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top