How to prevent implicit caching in Spring Boot 1.5.1 Hibernate - java

I am trying to understand why, after creating a new entity and persisting it whilst the application is running, when retrieving a list of these entities, the new entity should be retrieved from the database, but isn't?
For example:
I created a new entity (from the UI) and persisted it successfully like so:
#Repository
public class BaseDAOHibernate {
Session session;
public BaseDAOHibernate() {
session = HibernateUtils.getSessionFactory().openSession();
}
public void save(Object object) {
Transaction tx = session.beginTransaction();
session.save(object);
tx.commit();
}
...
I verified that the entity is persisted in the database.
Next, when I refresh the UI which lists these entities (for which I added a new one to the database) the new entity is not included and was not retrieved from the following:
#Repository
#SuppressWarnings("unchecked")
public class PasswordDAOHibernate extends BaseDAOHibernate implements PasswordDAO {
#Override
public Collection<Password> getPasswords() {
Query query = session.createQuery("select ...");
return query.list();
}
Here is the interface:
public interface PasswordDAO {
Password getPassword(Integer id);
Collection<Password> getPasswords();
Collection<Password> getPasswords(PasswordSearchParameters params);
Collection<PasswordType> getPasswordTypes();
}
...
Which is called from a controller:
#Controller
public class PasswordsController extends BaseControllerHelper {
#Autowired
private PasswordDAOHibernate passwordDAO;
#RequestMapping("/passwords.htm")
public void passwords(Map model,
HttpServletRequest request) {
Collection<Password> passwords = passwordDAO.getPasswords();
model.put("passwords", passwords);
}
Here is the configuration currently setup:
hibernate.cfg.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.bytecode.use_reflection_optimizer">false</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.password">password</property>
<property name="hibernate.connection.url">jdbc:mysql://host:3306/server</property>
<property name="hibernate.connection.username">username</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="show_sql">true</property>
<mapping class="com.crm.entity.User"></mapping>
<mapping class="com.crm.entity.Role"></mapping>
<mapping class="com.crm.entity.Property"></mapping>
<mapping class="com.crm.entity.Menu"></mapping>
<mapping class="com.crm.entity.Password"></mapping>
<mapping class="com.crm.entity.PasswordType"></mapping>
</session-factory>
</hibernate-configuration>
HibernateUtils.java
#Component
public class HibernateUtils {
private static final SessionFactory sessionFactory = buildSessionFactory();
private static SessionFactory buildSessionFactory() {
try {
return new Configuration().configure().buildSessionFactory();
}
catch (Throwable ex) {
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
public static void shutdown() {
getSessionFactory().close();
}
}
When I restart the server application the new entity displays in the list.
Is there some kind of implicit caching going on that I need to clear? Note, I have not implemented any explicit caching at this stage.

The problem is your dao, which is flawed in a very dangerous way. A Session isn't thread safe and shouldn't be shared. It should be opened for each action (or at least the current session should be used).
Plain Hibernate Solution
You DAO should look something like this.
private final SessionFactory sessionFactory;
protected BaseDAOHibernate(SessionFactory sessionFactory) {
this.sessionFactory=sessionFactory;
}
protected Session getSession() {
return this.sessionFactory.getCurrentSession();
}
public void save(Object object) {
getCurrentSession().save(object);
}
Now you specific dao should reuse the getSession method.
#Repository
#Transactional
public class PasswordDAOHibernate extends BaseDao implements PasswordDao {
#Autowired
public PasswordDAOHibernate(SessionFactory sessionFactory) {
super(sessionFactory);
}
#Override
public Collection<Password> getPasswords() {
return getSession.query("select ...", Password.class).list();
}
When doing so you probably will run into issues with an error stating that no session can be found due to no transaction (or something along those lines).
To fix this use Spring Boot (and some manual configuration).
First move the hibernate.connection properties to your application.properties and remove them from hibernate.cfg.xml.
spring.datasource.url=jdbc:mysql://host:3306/server
spring.datasource.username=username
spring.datasource.password
Now Spring will create a Datasource for you. Next remove your HibernateUtils and configure the SessionFactory using Springs LocalSessionFactoryBean.
#Bean
public LocalSessionFactoryBean sessionFactory(DataSource dataSource) {
LocalSessionFactoryBean factory = new LocalSessionFactoryBean();
factory.setDataSource(dataSource);
return factory;
}
You will also need the appropriate transaction manager
#Bean
public HibernateTransactionManager transactionManager(SessionFactory sessionFactory) {
return new HibernateTransactionManager(sessionFactory);
}
Now due to everything being setup in Spring, the injection of the SessionFactory and the #Transactional you will get a managed Session which will be properly opened and closed for you.
Controller Fix
Your controller is also flawed as you should be injecting a PasswordDao and not the concrete type (which would now fail due the the creation of a proxy for the transactions).
#Controller
public class PasswordsController extends BaseControllerHelper {
#Autowired
private PasswordDAO passwordDAO;
JPA Solution
However while all of this will probably work I would strongly suggest to use JPA and the EntityManager instead of the Session and SessionFactory approach.
To do so remove the LocalSessionFactoryBean and the HibernateTransactionManager and add the remaining properties of the hibernate.cfg.xml to the application.properties.
spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect
spring.jpa.show-sql=true
Next to the spring.datasource properties that is all you need and you can delete the hibernate.cfg.xml file.
Now instead of using the SessionFactory use the EntityManager in your dao.
public abstract class BaseDao {
#PersistenceContext
protected EntityManager em;
public void save(Object o) {
em.persist(o);
}
}
And your specific dao.
#Repository
#Transactional
public PasswordJpaDao extends BaseDao implements PasswordDao {
#Override
public Collection<Password> getPasswords() {
return em.createQuery("select ...", Password.class).getResultList();
}
Spring Data JPA Solution
When using JPA you could even drop your generic dao approach and implementations and use Spring Data JPA instead. Your whole PasswordDao would then look like
public interface PasswordDao extends JpaRepository<Password, Long> {}
All the crud functionality (findAll, findOne, save etc.) is available out-of-the-box. Creating queries is pretty easy and saves you from writing the boilerplate code.

You can use #Cacheable(false) on your entity
or disable the shared cache altogether for all your entities by adding this to your persistence.xml
<shared-cache-mode>NONE</shared-cache-mode>
For Hibernate you can also use
session.setCacheMode(CacheMode.IGNORE);
see this link for more information about the different cache levels in JPA

Related

Spring 4.3: Is it problematic to load a DataSource in a DAO instead of in a #Bean configuration

Is it ok to access a datasource in a Spring DAO in the following way (rather than configuring a bean and autowiring it):
#Repository
public class MyDAOImpl extends JdbcDaoSupport implements MyDAO {
#PostConstruct
private void initialize() {
JndiDataSourceLookup lookup = new JndiDataSourceLookup();
lookup.setResourceRef(true);
DataSource dataSource = lookup.getDataSource("jdbc/TWO");
super.setDataSource(dataSource);
}
public MyStuff getMyStuff(Long id) {
// getJdbcTemplate().query() etc
}
}
This works but is it ok or will this cause problems? Maybe create a connection leak or something else?
Thanks
(Edited for clarity)

Why do SimpleJdbcCall igronre #Transactional annotation

I want to do some DB related actions in service method. Initialy it looks like this:
#Override
#Transactional
public void addDirectory(Directory directory) {
//some cheks here
directoryRepo.save(directory);
rsdhUtilsService.createPhysTable(directory);
}
Firs method directoryRepo.save(directory); is just simple JPA save action, second one rsdhUtilsService.createPhysTable(directory); is JDBCTemplate stored procedure call from it's own service. The problem is: if any exceptions accures within JPA or SimpleJdbcCall action, transaction will rollback and nothig related to JPA won't be persited, but if exception occures only within JPA action, result of SimpleJdbcCall won't be affected by transaction rollback.
To illustrate this behaviour I've remove JAP action, mark #Transactional as (readOnly = true) and moved all JDBCTemplate related logic from another service to current one.
#Service
public class DirectoriesServiceImpl implements DirectoriesService {
private final DirectoryRepo directoryRepo;
private final MapSQLParamUtils sqlParamUtils;
private final JdbcTemplate jdbcTemplate;
#Autowired
public DirectoriesServiceImpl(DirectoryRepo directoryRepo, MapSQLParamUtils sqlParamUtils, JdbcTemplate jdbcTemplate) {
this.directoryRepo = directoryRepo;
this.sqlParamUtils = sqlParamUtils;
this.jdbcTemplate = jdbcTemplate;
}
#Override
#Transactional(readOnly = true)
public void addDirectory(Directory directory) {
directoryRepo.save(directory);
new SimpleJdbcCall(jdbcTemplate).withSchemaName("RSDH_DICT").withCatalogName("UTL_DICT")
.withFunctionName("create_dict")
.executeFunction(String.class, sqlParamUtils.getMapSqlParamForCreatePhysTable(directory));
}
}
As a result #Transactional annotation is ignored and I can see new records persisted in DB.
I've got only one DataSource configured via application.properties, and here is how JDBCTemlate configured
#Component
class MapSQLParamUtils {
private final DataSource dataSource;
#Autowired
MapSQLParamUtils(DataSource dataSource) {
this.dataSource = dataSource;
}
#Bean
public JdbcTemplate jdbcTemplate() {
return new JdbcTemplate(dataSource);
}
}
So my questions are: why do #Transactional ignored by SimpleJdbcCall and how to configure JPA and JDBCTemlate to use same transaction manager.
UPDATE:
This is how I use this service in controller
#RestController
#RequestMapping(value = "/api/v1/directories")
public class DirectoriesRESTControllerV1 {
private final DirectoriesService directoriesService;
#Autowired
public DirectoriesRESTControllerV1(DirectoriesService directoriesService) {
this.directoriesService = directoriesService;
}
#PostMapping
#PreAuthorize("hasPermission('DIRECTORIES_USER', 'W')")
public ResponseEntity createDirectory(#NotNull #RequestBody DirectoryRequestDTO createDirectoryRequestDTO) {
Directory directoryFromRequest = ServiceUtils.convertDtoToEntity(createDirectoryRequestDTO);
directoriesService.addDirectory(directoryFromRequest);
return ResponseEntity.noContent().build();
}
}
As mentioned earlier, the problem here is that JPA does not execute sql queries at once repository methods called. To enforce it you can use explicit entityManager.flush():
#Autowired
private javax.persistence.EntityManager entityManager;
...
#Override
#Transactional(readOnly = true)
public void addDirectory(Directory directory) {
directoryRepo.save(directory);
entityManager.flush();
new SimpleJdbcCall(jdbcTemplate).withSchemaName("RSDH_DICT").withCatalogName("UTL_DICT")
.withFunctionName("create_dict")
.executeFunction(String.class, sqlParamUtils.getMapSqlParamForCreatePhysTable(directory));
}
To see real SQL queries by hibernate you can enable option show_sql, in case if your application is spring-boot, this configuration enables it:
spring.jpa:
show-sql: true
properties:
hibernate:
format_sql: true
logging.level:
org.hibernate.SQL: DEBUG
Regarding transaction manager. In case if entityManager flush is not enough, you may need the composite transaction manager, that handles both JPA and DataSource. Spring data commons has ChainedTransactionManager. Note: you should be careful with it. I used it this way in my project:
#Bean(BEAN_CONTROLLER_TX)
public PlatformTransactionManager controllerTransactionManager(EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
#Bean(BEAN_ANALYTICS_TX)
public PlatformTransactionManager analyticsTransactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
/**
* Chained both 2 transaction managers.
*
* #return chained transaction manager for controller datasource and analytics datasource
*/
#Primary
#Bean
public PlatformTransactionManager transactionManager(
#Qualifier(BEAN_CONTROLLER_TX) PlatformTransactionManager controllerTransactionManager,
#Qualifier(BEAN_ANALYTICS_TX) PlatformTransactionManager analyticsTransactionManager) {
return new ChainedTransactionManager(controllerTransactionManager, analyticsTransactionManager);
}
Please try this :
#Transactional(rollbackFor = Exception.class)
public void addDirectory(Directory directory){
#Transactional only rolls back transactions for unchecked exceptions. For checked exceptions and their subclasses, it commits data. So although an exception is raised here, because it's a checked exception, Spring ignores it and commits the data to the database.
So if you throw an Exception or a subclass of it, always use the above with the #Transactional annotation to tell Spring to roll back transactions if a checked exception occurs.
It's very simple, just use the following with #Transactional:
#Transactional(rollbackFor = Exception.class)

why do Iget closed session outside of spring Context

I'm trying to configure spring with hibernate, and I noticed one thing I can not get: when I tryied to get Session object from Container.
Bean.getBean(GenericDao.class).getCurrentSession(); I can only get closed session.
If I will do same with openSession() method - I would receive valid session.
So the question is next: why? I ve been researching in google, but did not find answer. Do anybody knows?
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.io.Serializable;
import java.util.List;
#Transactional ( propagation = Propagation.REQUIRED, rollbackFor = { Throwable.class } )
public abstract class GenericDao {
#Autowired
protected SessionFactory sessionFactory;
public Session getCurrentSession() {
return sessionFactory.getCurrentSession();
}
public Session openSession() {
return sessionFactory.openSession();
}
public void saveOrUpdate(final Serializable object) {
getCurrentSession().saveOrUpdate(object);
}
public void delete(final Serializable object) {
getCurrentSession().delete(object);
}
public void save(final Serializable object) {
getCurrentSession().save(object);
}
public void update(final Serializable object) {
getCurrentSession().update(object);
}
public void merge(final Serializable object) {
getCurrentSession().merge(object);
}
#SuppressWarnings ( "unchecked" )
public <T> List<T> list(final Class<T> clazz) {
return getCurrentSession().createCriteria(clazz).list();
}
#SuppressWarnings ( "unchecked" )
public <T> T get(final Class<T> clazz, final Serializable id) {
return (T) getCurrentSession().get(clazz, id);
}
}
#Repository
#Primary
class GenericDaoImpl extends GenericDao {
}
1) This is because, using getCurrentSession() method returns the session bound to the context. But for this to work, we need to configure it in hibernate configuration file as below.
<property name="hibernate.current_session_context_class">thread</property>
thread context means current thread. The default value is jta context means an already existing jta transaction.
Since this session object (fetched using getCurrentSession()) belongs to the hibernate context, we don’t need to close it. Once the SessionFactory is closed, this session object gets closed.
The getCurrentSession() should be used for single threaded environment.
2) openSession() method always opens a new session. We should close this session object once we are done with all the database operations.
We should open a new session for each request in multi-threaded environment.
For more details related to thread safety and Hibernate Session & their usage, please see this.
Hibernate uses a the notion of current context class to determine the scope of the Session to use with getCurrentSession(). In pure Hibernate this can be thread whereby the Session is bound to the thread , jta where the session is bound to a JTA UserTransaction
When used with Spring you dont need to specify the hibernate.current_session_context_class property as Spring uses its own called SpringSessionContext. This binds the session to the ongoing transaction using whatever PlatformTransactionManager you are using
To use SessionFactory.getCurrentSession() make sure your call is inside a #Transactional method otherwise it wont work and you have to open the session manually using openSession()
#Transactional takes responsibility for opening and closing sessionFactory, you can use getCurrentSession without openSession as:
#SuppressWarnings ( "unchecked" )
public <T> List<T> list(final Class<T> clazz) {
return getCurrentSession().createCriteria(clazz).list();
}
if you configured transaction with #Annotation or in .xml file as here:
<bean id="sessionFactory" class="..."</bean>
<tx:annotation-driven proxy-target-class="true" transaction-manager="txManager" />
<bean id="txManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
and with #Transational and #Autowired as you do, it is ok
If you don't configure transaction, you can use openSession() only, and add closeSession when you want to finish session

cdi does not inject entitymanager

cdi don't injects entitymanager, always the nullpointer. Following configuration:
persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">
<persistence-unit name="tutoroo" transaction-type="JTA">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>java:/tutoroo</jta-data-source>
<properties>
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.format_sql" value="false" />
<property name="hibernate.hbm2ddl.auto" value="create-drop" />
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect" />
</properties>
</persistence-unit>
</persistence>
public class ProdutorEntityManager implements Serializable {
private EntityManagerFactory factory = Persistence.createEntityManagerFactory("tutoroo");
//private EntityManager entityManager = factory.createEntityManager();
#Produces
#PersistenceContext
#RequestScoped
public EntityManager criaEntityManager(){
return factory.createEntityManager();
}
public void dispose(#Disposes EntityManager em) {
em.close();
}
}
public class UsuarioDaoImp implements UsuarioDao {
#Inject
private EntityManager manager;
public void salvar(Usuario usuario) {
manager.persist(usuario);
}
}
When I debug the EntityManager UsuarioDaoImp class, this exception occurs: com.sun.jdi.InvocationException occurred invoking method.
I do not know what I'm doing wrong. Can anyone help?
Server is: jboss-as-7.1.1
First off, don't create the persistence units yourself in an app server, but let the server inject it for you.
Here's why, from JavaDocs:
The Persistence class is available in a Java EE container environment as well; however, support for the Java SE bootstrapping APIs is not required in container environments.
Not sure how jbos-as-7 behaves, but it is generally discouraged because of reasons such as loosing JTA support.
For simplicity, I assume you only have one persistence unit in your application. Please ask and I'll edit if you need examples for an application with multiple persistence units.
To simply use the entity manager in any CDI managed bean:
public class CDIBean {
// the container injects it
#PersistenceContext
private EntityManager em;
// just use it
public void someMethod(Entity someEntity) {
this.em.persist(someEntity);
}
}
That's all there is to it.
However, in many examples, a combination of producers / disposers are declared for various reasons. I bet this is where the confusion comes from. Some of the use cases:
To allow you to use #Inject EntityManger em; instead of #PersistenceContext EntityManager em;
// to make it available for injection using #Inject
public class CDIProducer {
// again, the container injects it
#PersistenceContext
private EntityManager em;
// this will have the default dependent scope
#Produces
public EntityManager em() {
return em;
}
public void dispose(#Disposes EntityManager em) {
em.close();
}
}
// to use it
public class CDIBean {
#Inject
private EntityManager em;
// just use it
public void someMethod(Entity someEntity) {
this.em.persist(someEntity);
}
}
or to bind an entity manager to a particular scope.
// to make it available for injection using #Inject, and bind it to the #RequestScope
public class CDIProducer {
// again, the container injects it
#PersistenceContext
private EntityManager em;
// this will be in the request scope
#Produces
#RequestScoped
public EntityManager em() {
return em;
}
public void dispose(#Disposes #RequestScoped EntityManager em) {
em.close();
}
}
// to use it
public class CDIBean {
#Inject
private EntityManager em;
// just use it
public void someMethod(Entity someEntity) {
this.em.persist(someEntity);
}
}
Finally, the method producers above can be converted to field producers. This is equivalent to the last example:
// to make it available for injection using #Inject, and bind it to the #RequestScope
public class CDIProducer {
#PersistenceContext
#Produces
#RequestScoped
private EntityManager em;
public void dispose(#Disposes #RequestScoped EntityManager em) {
em.close();
}
}
I think that #RequestScoped is not allowed as parameter injection.

PropertyPlaceholderConfigurer break my hibernate session

I have an issue in my Spring project when, after create my hibernateSession and TransactionManager I initilize a bean that extends PropertyPlaceholderConfigurer
I dont know why but in that moment my hibermnate session is ruin to be used for the rest of my DAO´s. The error that is throwing is this one.
Caused by: org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
Any suggestions?
I´m trying to extract from database some data to create a properties class and set to a class that I´ve create that extends PropertyPlaceholderConfigurer
This is the class
public class PropertiesSourceImpl extends PropertyPlaceholderConfigurer{
public ConfigurationSource configurationSource;
public Properties properties;
public void init() {
for (ConfigurationProperty prop : configurationSource.getEnabledConfigurationPropertiesByType(ConfigurationProperty.PropertyType.MAIL)) {
System.out.println(prop);
// properties.setProperty(prop.getPropertyKey(), prop.getPropertyValue());
}
}
public Properties getProperties() {
return properties;
}
#Required
public void setConfigurationSource(final ConfigurationSource configurationSource) {
this.configurationSource = configurationSource;
}
}
and here my bean definition
<bean id="propertiesSource" class="nl.greenvalley.digipolis.config.PropertiesSourceImpl" init-method="init">
<property name="configurationSource" ref="configurationSource"/>
</bean>
Finally I found a solution. Instead of use hibernate I just create a new session and transaction before the hibernate session and transaction would be created.
SessionFactory sessionFactory;
public void init() {
Session session = sessionFactory.openSession();
Transaction tx = null;
try {
Properties properties = new Properties();
tx = session.beginTransaction();
List<ConfigurationProperty> dbProperties = session.createCriteria(ConfigurationProperty.class).list();
for (ConfigurationProperty property : dbProperties) {
properties.setProperty(property.getPropertyKey(), property.getPropertyValue());
}
setProperties(properties);
tx.commit();
} finally {
session.close();
}
}

Categories

Resources