encrypting old data in database - java

I have added this in my application context file
<!-- Added to encrypt user identification fields using jasypt -->
<bean id="stringEncryptor" class="org.jasypt.encryption.pbe.StandardPBEStringEncryptor" lazy-init="false">
<property name="algorithm" value="PBEWithMD5AndDES" />
<property name="password" value="contactKey" />
</bean>
<bean id="hibernateEncryptor" class="org.jasypt.hibernate.encryptor.HibernatePBEStringEncryptor" lazy-init="false">
<!-- This property value must match "encryptorRegisteredName" used when defining hibernate user types -->
<property name="registeredName" value="jasyptHibernateEncryptor" />
<property name="encryptor" ref="stringEncryptor" />
</bean>`
This below coded added in hibernate mapping file
`<typedef name="encryptedString" class="org.jasypt.hibernate.type.EncryptedStringType">
<param name="encryptorRegisteredName">jasyptHibernateEncryptor</param>
</typedef>
We are using spring with Hibernate in to my application,but we want to implenting jasyptHibernateEncryptorin in to my application.
It's working fine when storing a new entry into database table and fetching the same entry, but problem here its how to encrypt my old data.

you create a new app that connects to the database, fetches all the existing rows and updates them one by one after encrypting the fields with your encryptor. After this update is done you can use the new typedef to handle these encrypted fields.

Ok, to elaborate more:
currently you mapped your entities/classes to a database with proterties NOT encrypted which looks something like this:
#Entity
public class Person {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private long id;
private String name;
}
If you plan to swith to an encrypted type (jasypt) you need to first encrypt all the current
values in the database with a code that looks like this:
public class Exec {
public static void main(String[] args) {
SessionFactory sf = HibernateUtil.getSessionFactory(true);
Session session = null;
try {
session = sf.openSession();
//configure the jasypt string encryptor - different type use different encryptors
StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
encryptor.setAlgorithm("PBEWithMD5AndDES");
encryptor.setPassword("123456");
encryptor.setKeyObtentionIterations(1000);
encryptor.initialize();
// get all unencrypted data from db and encrypt them - here just the name property of the Person is encrypted.
session.beginTransaction();
List<Person> persons = session.createQuery("select p from Person p").list();
for(Person pers : persons){
pers.setName(encryptor.encrypt(pers.getName()));
session.save(pers);
}
session.getTransaction().commit();
} catch (Exception ex) {
try {
ex.printStackTrace();
session.getTransaction().rollback();
} catch (Exception ex2) {
ex2.printStackTrace();
}
} finally {
session.close();
HibernateUtil.shutdown();
}
}
}
After you encrypted the values you need, switch the Person entity to use the encrypted type like this:
#org.hibernate.annotations.TypeDefs({
#org.hibernate.annotations.TypeDef(name="EncryptedString",
typeClass=EncryptedStringType.class,
parameters={#Parameter(name="algorithm",value="PBEWithMD5AndDES"),#Parameter(name="password",value="123456"),#Parameter(name="keyObtentionIterations",value="1000")})
})
#Entity
public class Person {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private long id;
#Type(type="EncryptedString")
private String name;
public long getId() {
return id;
}
// ... getters and setters
}
Make sure that the parameters of the encryptors are the same in the definistion and in the code:
same algorithm, same password, same key obtenation iteration parameter.
After that you can use the Person entity as usual since the encryption is transparent to the java persistence code you use.
A working example of this code you can checkout from svn with this command:
svn checkout http://hibernate-jasypt-database-encryption.googlecode.com/svn/trunk/ hibernate-jasypt-database-encryption-read-only
Good luck !

Related

Hibernate refresh() or getSingleResult() returns a cached entity (?)

I am using Hibernate 2.1 on a Wildfly (JBoss) 10 to fetch an entity from the Database. Here is the model of the entity:
#Entity
#Table(name = "account")
#NamedQuery(name = "Account.findAll", query = "SELECT a FROM Account a")
public class Account implements Serializable {
private int id;
private double balance;
private double bonus;
#Id
public int getId() {
return this.id;
}
// Rest of the fields/setters/getters ommited
}
I have implemented a REST API call that updates the balance of a given account. The account is first retrieved from the database, validated, then the balance is updated through a separate bean-managed transaction in MySQL. After this completes I am asked to do some other operation on the account, on which I need the (new) updated balance. Find code attached below:
#Stateless
#Path("/financial")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public class FinancialApi {
#Inject private AccountBean accountBean;
#Inject private AccountBalanceBean accountBalanceBean;
#POST
#Path("/balance/add")
public Response addBalance(TransactionProperties properties) {
Account account;
try {
account = accountBean.get(properties.getAccountId());
validateAccount(account);
} catch (ValidationException ex) {
return Response.status(Response.Status.BAD_REQUEST).build();
}
// Update balance
accountBalanceBean.updateBalance(account.getId(), properties.getAmount());
// Refresh from DB to retrieve updated balance
accountBean.refresh(account);
// Do stuff with refreshed account data here
return Response.ok().build();
}
}
The "DAO" EJB handling the account
#Stateless
#LocalBean
public class AccountEJBean {
#PersistenceContext(unitName = "my-unit-name")
private EntityManager em;
public Account get(int id) {
try {
return (Account) em.createQuery("SELECT a FROM Account a WHERE a.id = :id")
.setParameter("id", id)
.setMaxResults(1)
.getSingleResult();
} catch (NoResultException ex) {
return null;
}
}
public void refresh(Account account) {
em.refresh(account);
}
}
and a (severely reduced) sample of the EJB that uses bean-managed transactions to update account balance:
#Stateless
#LocalBean
#TransactionManagement(TransactionManagementType.BEAN)
public class AccountBalanceEJBean {
#PersistenceContext(unitName = "my-unit-name")
private EntityManager em;
#Resource
private UserTransaction transaction;
public boolean updateBalance(int id, float price) {
try {
transaction.begin();
getUpdateQuery(id, price).executeUpdate();
transaction.commit();
} catch (Exception ex) {
transaction.rolloback();
}
}
}
and here is my persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence">
<persistence-unit name="my-unit-name" transaction-type="JTA">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>java:/jboss/datasources/SQLDatasource</jta-data-source>
<class>mypackage.Account</class>
<properties>
<property name="hibernate.generate_statistics" value="false"/>
<property name="hibernate.connection.useUnicode" value="true"/>
<property name="hibernate.connection.characterEncoding" value="UTF-8"/>
</properties>
</persistence-unit>
</persistence>
This works perfectly fine until the part where I am trying to refresh account data from the database. At this point the account is refreshed with no issue on every case I have tried but the data of the account do not get updated. I am still retrieving the "stale" data that the account had when retrieved initially. After looking around for some time I figured it may be related to Hibernate caching mechanism. I have tried the following:
Detaching the entity on the get() method from the EntityManager and replacing refresh() with get()
Calling em.clear() and then retrieving the entity with a get() call again.
Attempting to clear the cache using em.getEntityManagerFactory().get().evictAll()
Setting hints "javax.persistence.cache.retrieveMode" and "javax.persistence.cache.storeMode" to BYPASS on the query of the get() method.
None of those seems to be working. I have tried updating the entity in my code and setting random values and every time the entity is refreshed in the initial retrieval state (not the one that is in the database, even if I alter different fields than balance not mentioned here).
From my understanding second-level cache is disabled by default on Hibernate and as far as I can tell that's not the case here. With this in my mind I move to the first-level cache but I can't seem to understand how to retrieve a Session (or if a session even exists here) from the entity manager in order to clear that cache which seems to be causing the problem. As a side note I am not sure of what's the point of having a first-level cache that doesn't get overriden by refresh() method and how this works in a distributed environment.
If that helps the Instance of the EntityManager that is injected is an instance of class org.jboss.as.jpa.container.TransactionScopedEntityManager.
Any ideas?
UPDATE: I have replicated the get() code in a second bean, let's call it AccountBean2 that's also injected into the API class. It seems to be working now. I am pretty confident it's a caching issue at this point but I am still not sure how to correct the initial issue so the question remains.

JPA-Repository save: Proceed saving after insert Constraint violation

i'm using JPA repository to save simple data objects to the database. To avoid duplicates i created a unique constraint on multiple fields. If now a duplicate according to the unique fields/constraint should be saved i want to catch the exception, log the object and the application should proceed and saves the next object. But here i always get this exception: "org.hibernate.AssertionFailure: null id in de.test.PeopleDBO entry (don't flush the Session after an exception occurs)".
In general i understand what hibernate is doing, but how i can revert the session or start a new session to proceed with saving of the next data objects. Please have a look to the code below:
PeopleDBO.java
#Entity
#Data
#Table(
name = "PEOPLE",
uniqueConstraints = {#UniqueConstraint(columnNames = {"firstname", "lastname"}})
public class PeopleDBO {
public PeopleDBO(String firstname, String lastname) {
this.firstname = firstname;
this.lastname = lastname;
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String firstname;
private String lastname;
}
The Test:
public void should_save_people_and_ignore_constraint_violation(){
final List<PeopleDBO> peopleList = Arrays.asList(
new PeopleDBO("Georg","Smith"),
new PeopleDBO("Georg","Smith"),
new PeopleDBO("Paul","Smith")
);
peopleList.forEach(p -> {
try {
peopleRepository.save(p);
} catch (DataIntegrityViolationException e) {
log.error("Could not save due to constraint violation: {}",p);
}
}
Assertions.assertThat(peopleRepository.count()).isEqualTo(2);
}
The problem is, that with saving of the second people the unique constraint gets violated. The error log happens, and with the next call of peopleRepository.save() the mentioned exception above is thrown:
"org.hibernate.AssertionFailure: null id in de.test.PeopleDBO entry (don't flush the Session after an exception occurs)"
How i can avoid this behaviour? How i can clean the session or start a new session?
Thanks a lot in advance
d.
--------- Edit / new idea ------
I just tried some things and have seen that i could implement a PeopleRepositoryImpl, like this:
#Service
public class PeopleRepositoryImpl {
final private PeopleRepository peopleRepository;
public PeopleRepositoryImpl(PeopleRepository peopleRepository) {
this.peopleRepository = peopleRepository;
}
#Transactional
public PeopleDBO save(PeopleDBO people){
return peopleRepository.save(people);
}
}
This is working pretty fine in my tests. ... what do you think?
One single transaction
The reason is that all inserts occur in one transaction. As this transaction is atomic, it either succeeds entirely or fails, there is nothing in-between.
The most clean solution is to check if a People exists before trying to insert it:
public interface PeopleRespository {
boolean existsByLastnameAndFirstname(String lastname, String firstname);
}
and then:
if (!peopleRepository.existsByLastnameAndFirstname(p.getLastname, p.getFirstname)) {
peopleRepository.save(p);
}
One transaction per people
An alternative is indeed to start a new transaction for each person. But I am not sure it will be more efficient, because there is an extra cost to create transaction.

Hibernate : Table doesn't get updated

I'm having trouble with my hibernate transaction. After execution , table has no values in it and is not updated at all. All my other updates work properly.
Here's the mapping
<class name="LastDownloadedMessage" table="t_imap_lastmsguid">
<id name="id" column="id" ><generator class="increment"/></id>
<property name="lastDownloadedMessageUid"><column name="last_msg_uid" /></property>
<property name="lastUidNext"><column name="next_msg_uid" /></property>
<property name="folder"><column name="folder_name" /></property>
<property name="cred"><column name="credential" /></property>
</class>
This is the POJO object :
public class LastDownloadedMessage {
Integer id;
private String lastDownloadedMessageUid;
private String lastUidNext;
private String folder;
private String cred;
//GETTERS AND SETTERS HERE
public LastDownloadedMessage() {
super();
}
public LastDownloadedMessage(String lastDownloadedMessageUid,
String lastUidNext) {
super();
this.lastDownloadedMessageUid = lastDownloadedMessageUid;
this.lastUidNext = lastUidNext;
}
}
and this is the function which is doing the update.
Session ssn=HibernateSessionFactory.openSession();
Transaction txn = ssn.beginTransaction();
Query query = ssn.createQuery("update LastDownloadedMessage e set e.lastDownloadedMessageUid = :luid , e.lastUidNext = :nextUid where e.folder =:folder and e.cred = :cred");
query.setParameter("luid",last_downloaded_msg_uid);
query.setParameter("nextUid", uid_next);
query.setParameter("folder", folder);
query.setParameter("cred", credential);
int result = query.executeUpate();
txn.commit();
ssn.flush();
ssn.close();
The function appears to execute properly with no errors . What could be the issue ?
Based on the comments, it seems to me you are confusing UPDATE with INSERT.
If result is zero, it means your WHERE clause didn't match anything, so there's nothing to update.
Also, why are you trying to issue an UPDATE/INSERT on a known (mapped) entity? All you have to do is set your values on your object (LastDownloadedMessage) and then execute a persist on your EntityManager. Browse around the web for EntityManager.persist.

Hibernate HQL returns stale data?

My Hibernate HQL query seems to be returning stale data.
I have a simple java class called Account, instances of which map onto a single database table with two varchar columns, username and surname.
If I run a HQL query such as:
List<?> accountList = session.createQuery("from Account where surname is null").list();
I get back a List of Account objects, as expected (some of the rows in the table indeed have null surname fields).
I then set the surname on the returned objects to some non-null value:
Iterator<?> accountIter = accountList.iterator();
while (accountIter.hasNext()) {
Account account = (Account) accountIter.next();
log("Adding surname of Jones to : " + account.getUsername());
account.setSurname("Jones");
}
At this point, if I ran the HQL query again, I would expect to get back an empty List (as all surnames should be non-null),
but instead I get back the same objects as when I ran the query the first time. This is not what I expected.
Quoting from the Hibernate docs:
http://docs.jboss.org/hibernate/orm/4.1/manual/en-US/html_single/
"there are absolutely no guarantees about when the Session executes the JDBC calls,
only the order in which they are executed. However, Hibernate does guarantee that the
Query.list(..) will never return stale or incorrect data."
This seems contrary to the behaviour of my code. Looking at the program output in Listing 4 below, the SQL Update statement happens after all the select statements, so the last select returns incorrect data.
Can anyone shed light on what is going on, or what I am doing wrong?
If I surround the setting of the surnames with a transaction, and perform a session.saveOrUpdate(account)
it all works, but I thought that this was not required.
I would like my code to only deal with the domain classes if possible, and be free
of persistence code as much as possible.
I am using Hibernate 4.1.8.Final, with Java 1.6
My full code listing is below:
Listing 1: Main.java:
package uk.ac.york.cserv.hibernatetest;
import java.util.Iterator;
import java.util.List;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class Main {
private static SessionFactory sf;
Session session;
public static void main(String[] args) {
Main main = new Main();
main.doExample();
}
public Main() {
sf = new Configuration()
.configure("hibernate-ora.cfg.xml")
.buildSessionFactory();
session = sf.openSession();
}
public void closeSession() {
session.flush();
session.close();
}
public List<?> getAccountList() {
return session.createQuery("from Account where surname is null").list();
}
public void printAccountList(List<?> accountList) {
Iterator<?> accountIter = accountList.iterator();
while (accountIter.hasNext()) {
System.out.println(accountIter.next());
}
}
public void log(String msg) {
System.out.println(msg);
}
public void doExample() {
log("Print all accounts with null surnames...");
printAccountList(getAccountList());
log("Adding surnames to accounts that have null surnames...");
//session.beginTransaction();
Iterator<?> accountIter = getAccountList().iterator();
while (accountIter.hasNext()) {
Account account = (Account) accountIter.next();
log("Adding surname of Jones to : " + account.getUsername());
account.setSurname("Jones");
//session.saveOrUpdate(account);
}
//session.getTransaction().commit();
log("Again print all accounts that have null surnames (should be none)...");
printAccountList(getAccountList());
closeSession();
}
}
Listing 2: Account.java:
package uk.ac.york.cserv.hibernatetest;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name="ACCOUNTS")
public class Account {
#Id
#Column(name = "USERNAME", unique = true, nullable = false)
private String username;
#Column(name = "SURNAME")
private String surname;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
#Override
public String toString() {
return "Account [username=" + username + ", surname=" + surname + "]";
}
}
Listing 3: Hibernate-ora.cfg.xml:
<hibernate-configuration>
<session-factory>
<!-- Database connection settings -->
<property name="connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
<property name="connection.url">jdbc:oracle:thin:#testhost:1521:test</property>
<property name="connection.username">testschema</property>
<property name="connection.password">testpassword</property>
<!-- JDBC connection pool (use the built-in) -->
<property name="connection.pool_size">1</property>
<!-- SQL dialect -->
<property name="dialect">org.hibernate.dialect.Oracle10gDialect</property>
<!-- Disable the second-level cache -->
<property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
<!-- Echo all executed SQL to stdout -->
<property name="show_sql">true</property>
<!-- Names of the annotated classes -->
<mapping class="uk.ac.york.cserv.hibernatetest.Account"/>
</session-factory>
</hibernate-configuration>
Listing 4: Output of the program:
Print all accounts with null surnames...
Hibernate: select account0_.USERNAME as USERNAME0_, account0_.SURNAME as SURNAME0_ from ACCOUNTS account0_ where account0_.SURNAME is null
Account [username=user2, surname=null]
Adding surnames to accounts that have null surnames...
Hibernate: select account0_.USERNAME as USERNAME0_, account0_.SURNAME as SURNAME0_ from ACCOUNTS account0_ where account0_.SURNAME is null
Adding surname of Jones to : user2
Again print all accounts that have null surnames (should be none)...
Hibernate: select account0_.USERNAME as USERNAME0_, account0_.SURNAME as SURNAME0_ from ACCOUNTS account0_ where account0_.SURNAME is null
Account [username=user2, surname=Jones]
Hibernate: update ACCOUNTS set SURNAME=? where USERNAME=?
There is nothing strange about the Hibernate behavior you're describing
"At this point, if I ran the HQL query again, I would expect to get back an empty List (as all surnames should be non-null), but instead I get back the same objects as when I ran the query the first time. This is not what I expected."
At that point, when you run the HQL query again, you haven't done anything concerning the database so far. This is the reason why you're obtaining what you call 'stale' data but it's in fact the most current version of what is still unmodified in the table
If you issue the saveOrUpdate command and close the transaction the changes you have done in your Java class are persisted to database so that the new HQL query executions show the updated data
I think you're misunderstanding the way Hibernate works in this use case. Precisely because "Hibernate does guarantee that the Query.list(..) will never return stale or incorrect data." you see an updated version of the data coming from the database, from the database point of view your changes in your Java class are the 'stale' ones and are replaced by new "fresh" real data coming from the original still unmodified source

Hibernate/Spring: failed to lazily initialize - no session or session was closed

For an answer scroll down to the end of this...
The basic problem is the same as asked multiple time. I have a simple program with two POJOs Event and User - where a user can have multiple events.
#Entity
#Table
public class Event {
private Long id;
private String name;
private User user;
#Column
#Id
#GeneratedValue
public Long getId() {return id;}
public void setId(Long id) { this.id = id; }
#Column
public String getName() {return name;}
public void setName(String name) {this.name = name;}
#ManyToOne
#JoinColumn(name="user_id")
public User getUser() {return user;}
public void setUser(User user) {this.user = user;}
}
The User:
#Entity
#Table
public class User {
private Long id;
private String name;
private List<Event> events;
#Column
#Id
#GeneratedValue
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
#Column
public String getName() { return name; }
public void setName(String name) { this.name = name; }
#OneToMany(mappedBy="user", fetch=FetchType.LAZY)
public List<Event> getEvents() { return events; }
public void setEvents(List<Event> events) { this.events = events; }
}
Note: This is a sample project. I really want to use Lazy fetching here.
Now we need to configure spring and hibernate and have a simple basic-db.xml for loading:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close" scope="thread">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://192.168.1.34:3306/hibernateTest" />
<property name="username" value="root" />
<property name="password" value="" />
<aop:scoped-proxy/>
</bean>
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="thread">
<bean class="org.springframework.context.support.SimpleThreadScope" />
</entry>
</map>
</property>
</bean>
<bean id="mySessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean" scope="thread">
<property name="dataSource" ref="myDataSource" />
<property name="annotatedClasses">
<list>
<value>data.model.User</value>
<value>data.model.Event</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">create</prop>
</props>
</property>
<aop:scoped-proxy/>
</bean>
<bean id="myUserDAO" class="data.dao.impl.UserDaoImpl">
<property name="sessionFactory" ref="mySessionFactory" />
</bean>
<bean id="myEventDAO" class="data.dao.impl.EventDaoImpl">
<property name="sessionFactory" ref="mySessionFactory" />
</bean>
</beans>
Note: I played around with the CustomScopeConfigurer and SimpleThreadScope, but that didnt change anything.
I have a simple dao-impl (only pasting the userDao - the EventDao is pretty much the same - except with out the "listWith" function:
public class UserDaoImpl implements UserDao{
private HibernateTemplate hibernateTemplate;
public void setSessionFactory(SessionFactory sessionFactory) {
this.hibernateTemplate = new HibernateTemplate(sessionFactory);
}
#SuppressWarnings("unchecked")
#Override
public List listUser() {
return hibernateTemplate.find("from User");
}
#Override
public void saveUser(User user) {
hibernateTemplate.saveOrUpdate(user);
}
#Override
public List listUserWithEvent() {
List users = hibernateTemplate.find("from User");
for (User user : users) {
System.out.println("LIST : " + user.getName() + ":");
user.getEvents().size();
}
return users;
}
}
I am getting the org.hibernate.LazyInitializationException - failed to lazily initialize a collection of role: data.model.User.events, no session or session was closed at the line with user.getEvents().size();
And last but not least here is the Test class I use:
public class HibernateTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("basic-db.xml");
UserDao udao = (UserDao) ac.getBean("myUserDAO");
EventDao edao = (EventDao) ac.getBean("myEventDAO");
System.out.println("New user...");
User user = new User();
user.setName("test");
Event event1 = new Event();
event1.setName("Birthday1");
event1.setUser(user);
Event event2 = new Event();
event2.setName("Birthday2");
event2.setUser(user);
udao.saveUser(user);
edao.saveEvent(event1);
edao.saveEvent(event2);
List users = udao.listUserWithEvent();
System.out.println("Events for users");
for (User u : users) {
System.out.println(u.getId() + ":" + u.getName() + " --");
for (Event e : u.getEvents())
{
System.out.println("\t" + e.getId() + ":" + e.getName());
}
}
((ConfigurableApplicationContext)ac).close();
}
}
and here is the Exception:
1621 [main] ERROR org.hibernate.LazyInitializationException - failed to lazily initialize a collection of role: data.model.User.events, no session or session was closed
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: data.model.User.events, no session or session was closed
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:380)
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:372)
at org.hibernate.collection.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:119)
at org.hibernate.collection.PersistentBag.size(PersistentBag.java:248)
at data.dao.impl.UserDaoImpl.listUserWithEvent(UserDaoImpl.java:38)
at HibernateTest.main(HibernateTest.java:44)
Exception in thread "main" org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: data.model.User.events, no session or session was closed
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:380)
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:372)
at org.hibernate.collection.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:119)
at org.hibernate.collection.PersistentBag.size(PersistentBag.java:248)
at data.dao.impl.UserDaoImpl.listUserWithEvent(UserDaoImpl.java:38)
at HibernateTest.main(HibernateTest.java:44)
Things tried but did not work:
assign a threadScope and using beanfactory (I used "request" or "thread" - no difference noticed):
// scope stuff
Scope threadScope = new SimpleThreadScope();
ConfigurableListableBeanFactory beanFactory = ac.getBeanFactory();
beanFactory.registerScope("request", threadScope);
ac.refresh();
...
Setting up a transaction by getting the session object from the deo:
...
Transaction tx = ((UserDaoImpl)udao).getSession().beginTransaction();
tx.begin();
users = udao.listUserWithEvent();
...
getting a transaction within the listUserWithEvent()
public List listUserWithEvent() {
SessionFactory sf = hibernateTemplate.getSessionFactory();
Session s = sf.openSession();
Transaction tx = s.beginTransaction();
tx.begin();
List users = hibernateTemplate.find("from User");
for (User user : users) {
System.out.println("LIST : " + user.getName() + ":");
user.getEvents().size();
}
tx.commit();
return users;
}
I am really out of ideas by now. Also, using the listUser or listEvent just work fine.
Step forward:
Thanks to Thierry I got one step further (I think). I created the MyTransaction class and do my whole work in there, getting everything from spring. The new main looks like this:
public static void main(String[] args) {
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("basic-db.xml");
// getting dao
UserDao udao = (UserDao) ac.getBean("myUserDAO");
EventDao edao = (EventDao) ac.getBean("myEventDAO");
// gettting transaction template
TransactionTemplate transactionTemplate = (TransactionTemplate) ac.getBean("transactionTemplate");
MyTransaction mt = new MyTransaction(udao, edao);
transactionTemplate.execute(mt);
((ConfigurableApplicationContext)ac).close();
}
Unfortunately now there is a null-pointer Exception #: user.getEvents().size(); (in the daoImpl).
I know that it should not be null (neither from the output in the console nor from the db layout).
Here is the console output for more information (I did a check for user.getEvent() == null and printed "EVENT is NULL"):
New user...
Hibernate: insert into User (name) values (?)
Hibernate: insert into User (name) values (?)
Hibernate: insert into Event (name, user_id) values (?, ?)
Hibernate: insert into Event (name, user_id) values (?, ?)
Hibernate: insert into Event (name, user_id) values (?, ?)
List users:
Hibernate: select user0_.id as id0_, user0_.name as name0_ from User user0_
1:User1
2:User2
List events:
Hibernate: select event0_.id as id1_, event0_.name as name1_, event0_.user_id as user3_1_ from Event event0_
1:Birthday1 for 1:User1
2:Birthday2 for 1:User1
3:Wedding for 2:User2
Hibernate: select user0_.id as id0_, user0_.name as name0_ from User user0_
Events for users
1:User1 --
EVENT is NULL
2:User2 --
EVENT is NULL
You can get the sample project from http://www.gargan.org/code/hibernate-test1.tgz (it's an eclipse/maven project)
The solution (for console applications)
There are actually two solutions for this problem - depending on your environment:
For a console application you need a transaction template which captures the actutal db logic and takes care of the transaction:
public class UserGetTransaction implements TransactionCallback{
public List users;
protected ApplicationContext context;
public UserGetTransaction (ApplicationContext context) {
this.context = context;
}
#Override
public Boolean doInTransaction(TransactionStatus arg0) {
UserDao udao = (UserDao) ac.getBean("myUserDAO");
users = udao.listUserWithEvent();
return null;
}
}
You can use this by calling:
TransactionTemplate transactionTemplate = (TransactionTemplate) context.getBean("transactionTemplate");
UserGetTransaction mt = new UserGetTransaction(context);
transactionTemplate.execute(mt);
In order for this to work you need to define the template class for spring (ie. in your basic-db.xml):
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"/>
</bean>
Another (possible) solution
thanks andi
PlatformTransactionManager transactionManager = (PlatformTransactionManager) applicationContext.getBean("transactionManager");
DefaultTransactionAttribute transactionAttribute = new DefaultTransactionAttribute(TransactionDefinition.PROPAGATION_REQUIRED);
transactionAttribute.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE);
TransactionStatus status = transactionManager.getTransaction(transactionAttribute);
boolean success = false;
try {
new UserDataAccessCode().execute();
success = true;
} finally {
if (success) {
transactionManager.commit(status);
} else {
transactionManager.rollback(status);
}
}
The solution (for servlets)
Servlets are not that big of a problem. When you have a servlet you can simply start and bind a transaction at the beginning of your function and unbind it again at the end:
public void doGet(...) {
SessionFactory sessionFactory = (SessionFactory) context.getBean("sessionFactory");
Session session = SessionFactoryUtils.getSession(sessionFactory, true);
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
// Your code....
TransactionSynchronizationManager.unbindResource(sessionFactory);
}
I think you should not use the hibernate session transactional methods, but let spring do that.
Add this to your spring conf:
<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="mySessionFactory" />
</bean>
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="txManager"/>
</bean>
and then I would modify your test method to use the spring transaction template:
public static void main(String[] args) {
// init here (getting dao and transaction template)
transactionTemplate.execute(new TransactionCallback() {
#Override
public Object doInTransaction(TransactionStatus status) {
// do your hibernate stuff in here : call save, list method, etc
}
}
}
as a side note, #OneToMany associations are lazy by default, so you don't need to annotate it lazy. (#*ToMany are LAZY by default, #*ToOne are EAGER by default)
EDIT: here is now what is happening from hibernate point of view:
open session (with transaction start)
save a user and keep it in the session (see the session cache as an entity hashmap where the key is the entity id)
save an event and keep it in the session
save another event and keep it in the session
... same with all the save operations ...
then load all users (the "from Users" query)
at that point hibernate see that it has already the object in its session, so discard the one it got from the request and return the one from the session.
your user in the session does not have its event collection initialized, so you get null.
...
Here are some points to enhance your code:
in your model, when collection ordering is not needed, use Set, not List for your collections (private Set events, not private List events)
in your model, type your collections, otherwise hibernate won't which entity to fetch (private Set<Event> events)
when you set one side of a bidirectional relation, and you wish to use the mappedBy side of the relation in the same transaction, set both sides. Hibernate will not do it for you before the next tx (when the session is a fresh view from the db state).
So to address the point above, either do the save in one transaction, and the loading in another one :
public static void main(String[] args) {
// init here (getting dao and transaction template)
transactionTemplate.execute(new TransactionCallback() {
#Override
public Object doInTransaction(TransactionStatus status) {
// save here
}
}
transactionTemplate.execute(new TransactionCallback() {
#Override
public Object doInTransaction(TransactionStatus status) {
// list here
}
}
}
or set both sides:
...
event1.setUser(user);
...
event2.setUser(user);
...
user.setEvents(Arrays.asList(event1,event2));
...
(Also do not forget to address the code enhancement points above, Set not List, collection typing)
In case of Web application, it is also possible to declare a special Filter in web.xml, that will do session-per-request:
<filter>
<filter-name>openSessionInViewFilter</filter-name>
<filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>openSessionInViewFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
After that you can lazyload your data anytime during the request.
I got here looking for a hint regarding a similar problem. I tried the solution mentioned by Thierry and it didnt work. After that I tried these lines and it worked:
SessionFactory sessionFactory = (SessionFactory) context.getBean("sessionFactory");
Session session = SessionFactoryUtils.getSession(sessionFactory, true);
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
Indeed what I'm doing is a batch process that must leverage Spring existings managers/services. After loading the context and doing some invocations I founded the famous issue "failed to lazily initialize a collection". Those 3 lines solved it for me.
The issue is that your dao is using one hibernate session but the lazy load of the user.getName (I assume that is where it throws) is happening outside that session -- either not in a session at all or in another session. Typically we open up a hibernate session before we make DAO calls and don't close it until we are done with all lazy loads. Web requests are usually wrapped in a big session so these problems do not happen.
Typically we have wrapped our dao and lazy calls in a SessionWrapper. Something like the following:
public class SessionWrapper {
private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sessionFactory) {
this.hibernateTemplate = new HibernateTemplate(sessionFactory);
}
public <T> T runLogic(Callable<T> logic) throws Exception {
Session session = null;
// if the session factory is already registered, don't do it again
if (TransactionSynchronizationManager.getResource(sessionFactory) == null) {
session = SessionFactoryUtils.getSession(sessionFactory, true);
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
}
try {
return logic.call();
} finally {
// if we didn't create the session don't unregister/release it
if (session != null) {
TransactionSynchronizationManager.unbindResource(sessionFactory);
SessionFactoryUtils.releaseSession(session, sessionFactory);
}
}
}
}
Obviously the SessionFactory the same SessionFactory that was injected into your dao.
In your case, you should wrap the entire listUserWithEvent body in this logic. Something like:
public List listUserWithEvent() {
return sessionWrapper.runLogic(new Callable<List>() {
public List call() {
List users = hibernateTemplate.find("from User");
for (User user : users) {
System.out.println("LIST : " + user.getName() + ":");
user.getEvents().size();
}
}
});
}
You will need to inject the SessionWrapper instance into your daos.
Interesting!
I had the same problem in a #Controller's #RequestMapping handler method.
The simple solution was to add a #Transactional annotation to the handler method so that the session is kept open for the whole duration of the method body execution
Easiest solution to implement:
Within the scope of the session[inside the API annotated with #Transactional], do the following:
if A had a List<B> which is lazily loaded, simply call an API which makes sure the List is loaded
What's that API ?
size(); API of the List class.
So all that's needed is:
Logger.log(a.getBList.size());
This simple call of logging the size makes sure it gets the whole list before calculating the size of the list. Now you will not get the exception !
What worked for us in JBoss was the solution #2 taken from this site at Java Code Geeks.
Web.xml:
<filter>
<filter-name>ConnectionFilter</filter-name>
<filter-class>web.ConnectionFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ConnectionFilter</filter-name>
<url-pattern>/faces/*</url-pattern>
</filter-mapping>
ConnectionFilter:
import java.io.IOException;
import javax.annotation.Resource;
import javax.servlet.*;
import javax.transaction.UserTransaction;
public class ConnectionFilter implements Filter {
#Override
public void destroy() { }
#Resource
private UserTransaction utx;
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
try {
utx.begin();
chain.doFilter(request, response);
utx.commit();
} catch (Exception e) { }
}
#Override
public void init(FilterConfig arg0) throws ServletException { }
}
Maybe it would work with Spring too.

Categories

Resources