how to update many-to-many collection (lazy-loading) in hibernate/spring? - spring

I have three tables and two classes.there are many-to-many relationship between the two tables/classes.
OYS_USER -->Many-to-Many with 'oys_lesson'
OYS_LESSON-->Many-to-Many with 'oys_user'
OYS_LESSON_STUDENT-->relationship
in User.class
#ManyToMany(cascade= CascadeType.ALL,fetch=FetchType.LAZY)
#JoinTable(name="oys_lesson_student",
joinColumns = { #JoinColumn(name="student_id",referencedColumnName="id")},
inverseJoinColumns = {#JoinColumn(name="lesson_id",referencedColumnName="id") })
#Fetch(FetchMode.SUBSELECT)
private Set<Lesson> studentLessons;
in Lesson.class
#ManyToMany(cascade=CascadeType.ALL,fetch=FetchType.LAZY)
#JoinTable(name = "oys_lesson_student", joinColumns = { #JoinColumn(name = "lesson_id", referencedColumnName = "id") }, inverseJoinColumns = { #JoinColumn(name = "student_id", referencedColumnName = "id") })
#Fetch(FetchMode.SUBSELECT)
private Set<User> lessonStudents;
I updated collection with below codes:
#Override
public void addLessonToStudent(Lesson lesson) {
// TODO Auto-generated method stub
String username = SecurityContextHolder.getContext()
.getAuthentication().getName();
Criteria criteria = openSession().createCriteria(User.class)
.add(Restrictions.eq("username", username));
User user=(User) criteria.uniqueResult();
log.info("'"+lesson.getLessonName()+"' lesson added to '"+user.getUsername()+"'");
/*user.getStudentLessons().add(lesson);
updateUser(user);
*/
lesson.getLessonStudents().add(user);
updateLesson(lesson);
} catch (HibernateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
#Override
public void updateLesson(Lesson lesson) {
log.info(lesson.getLessonName()+" updated.");
openSession().update(lesson);
}
When I added current user to lesson's collection (or lesson to current user's collection).
update/insert query does not appear in hibernate statistics(console-log).
So a record can not be added to any collection.
Basically,I want to add new object to many to many collection.What am I doing wrong?
I use hibernate 4.3.5.Final (to get rid of lazy loading exception)
I use org.springframework.orm.hibernate4.support.OpenSessionInViewFilter for rid of lazy loading exception).
I tried CascadeType.EAGER with FetchType.JOIN.But did not change the results.update/insert query does not appear in hibernate statistics(console-log).
I use below properties for hibernate in application-context.xml(spring-hibernate-configuration):
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.generate_statistics">true</prop>
<prop key="hibernate.cache.use_second_level_cache">true</prop>
<prop key="hibernate.cache.use_query_cache">true</prop>
<prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory
</prop>
<prop key="hibernate.enable_lazy_load_no_trans">true</prop>
<prop key="use_sql_comments">true</prop>
<prop key="hibernate.bytecode.use_reflection_optimizer">true</prop>
<prop key="hibernate.connection.autocommit">true</prop>
<prop key="net.sf.ehcache.configurationResourceName">/myehcache.xml</prop>
</props>
</property>
and spring txManager:
<!-- Enable the configuration of transactional behavior based on annotations -->
<tx:annotation-driven transaction-manager="txManager" />
<!-- Transaction Manager is defined -->
<bean id="txManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="SessionFactory" />
</bean>
I use spring/security,hibernate 4.3.5.Final,jpa2.1,jsf2.2. Thanks in advance.

You are almost there.
Firstly, you need to decide which site of your reletionship is 'in control'. Is the user or is it the lesson. And yo define the join table only on one of them so for example:
- you leave the user as it is
- on lesson you changed
#ManyToMany(mappedBy="studentLessons" ...)
Then, you need to add the reletionship manually to both sites of the relationship (in reality adding to the one in charge of mapping is typically enought).
so
user.getUserLesseson().add(lesson);
lesson.getLessonStudents().add(user);
if both are issued inside transaction it will result in the insert

Related

Exception Illegal Argument - unknown entity JPA - Additional package to scan for entities in application-context.xml is not taken into consideration

I am working on a Spring application which has a persistence unit configured in the application-context.xml. I need to add an additional package in in order to use new entities.
Even though this part of the persistence.xml file looks like below, my entities from the additional package are not seen by the application and I get an exception saying that the entity is unknown.
<bean id="transactionManager_students" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactoryStudents" />
<qualifier value="clientTransaction" />
</bean>
<bean id="entityManagerFactoryStudents"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="datasource_College" />
<property name="jpaVendorAdapter" ref="hibernateJpaVendorAdapter" />
<property name="packagesToScan">
<list>
<value>com.load.model</value>
<value>com.students.entity</value>
</list>
</property>
<property name="persistenceUnitName" value="unit_stud" />
<property name="jpaProperties">
<props>
<prop key="hibernate.generate_statistics">true</prop>
<prop key="hibernate.cache.use_query_cache">false</prop>
<prop key="hibernate.cache.use_second_level_cache">false</prop>
<prop key="hibernate.hbm2ddl.auto">none</prop>
<prop key="hibernate.show_sql">false</prop>
</props>
</property>
</bean>  
I also have to mention that I annotated the entities with #Entity and in the class where I am operating on the entities I have this ( the row with em.persist(student) is giving me the error )
#PersistenceContext(unitName = "unit_stud")
public EntityManager em;
public Student student;
#Transactional(value = "clientTransaction", propagation = Propagation.REQUIRED)
public long persistStudentObject() {
long studentId = 0;
try
{
logger.debug("Start Persisting...");
em.persist(student);
// unique ID
studentId = student.getId();
logger.debug("Persisting OK...");
}
catch (PersistenceException persistenceException)
{
logger.error("PersistenceException occur", persistenceException);
}
}
return studentId ;
}
The entity:
package com.students.entity;
#Entity
#Table(name = "STUDENTS", schema = "DEMO", catalog = "")
public class Student{
private long id;
private String firstname;
private String name;
private String streetnumber;
private String zipcodecity;
Can anyone help me? I do not know what to do in order to make my entities visible.

org.hibernate.HibernateException: Illegal attempt to associate a collection with two open sessions in hibernate 4

I am using JTA transaction manager in hibernate 4.Does anyone has any idea about the issue. While executing flush I am facing the error.
Below is the Piece of code where i am facing the above issue.
public void saveOrUpdateEvent(Event event)
{
event = DomainReferenceMapper.map(event);
Session session = getSessionFactory().getCurrentSession();
session.saveOrUpdate(event);
session.flush();
XML:
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.Oracle9Dialect</prop>
<prop key="hibernate.show_sql">false</prop>
<!-- prop key="hibernate.query.factory_class">org.hibernate.hql.classic.ClassicQueryTranslatorFactory</prop-->
<!-- Package was changed in the updgration of hibernate -->
<prop key="hibernate.query.factory_class">org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory</prop>
<prop key="hibernate.cache.provider_class">org.hibernate.cache.TreeCacheProvider</prop>
<!-- prop key="hibernate.cache.use_query_cache">true</prop -->
<!--
Added for hibernate upgradation
-->
<prop key="hibernate.cache.use_second_level_cache">false</prop>
<!--<prop key="hibernate.enable_lazy_load_no_trans">true</prop>
--><prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</prop>
<prop key="hibernate.transaction.jta.platform">org.hibernate.service.jta.platform.internal.WeblogicJtaPlatform</prop>
</props>
</property>
public void saveOrUpdateEvent(Event event)
{
event = DomainReferenceMapper.map(event);
Session session = null ;
Transaction tran = null;
try {
session = getSessionFactory().getCurrentSession();
tran = session.beginTransaction();
session.saveOrUpdate(event);
session.flush();
tran.commit();
} catch(Exception ex ) {
ex.printstacktrance();
}

Spring: change EntityManager dynamically

for some reason the database (ugly!) that I should use contains all the tables twice; every table are duplicated into these modes: DB1_<table> and DB2_<table>.
The structures of these databases are the same!
The application that I am realizing uses Spring + Hibernate and should permit to the users to change the database on runtime; this mean that a user can start the application using database DB1 and after some minutes change to DB2, return to DB1 and so on.
I have tried to extend DefaultNamingStrategy for every databases:
// I have create also DB2
public class DB1 extends DefaultNamingStrategy {
private static final long serialVersionUID = 676544180324515651L;
#Override
public String tableName(String tableName) {
return "DB1_" + tableName;
}
}
and set the naming strategy through the property hibernate.ejb.naming_strategy of jpa dinamically but for a reason that I can't understand I can change the naming strategy only once, and all next callings trown an Exception.
Someone know why?
Configuration of entityManagerFactory:
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="it.perfectquiz.entity" />
<property name="persistenceProviderClass" value="org.hibernate.jpa.HibernatePersistenceProvider" />
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
</props>
</property>
</bean>
How I try to change naming strategy:
XmlWebApplicationContext context = (XmlWebApplicationContext) ContextLoader.getCurrentWebApplicationContext();
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) context.getBeanFactory();
GenericBeanDefinition entityManager = (GenericBeanDefinition) beanFactory.getBeanDefinition("entityManagerFactory");
ManagedProperties jpaProperties = (ManagedProperties) entityManager.getPropertyValues().get("jpaProperties");
TypedStringValue namingStrategy = (TypedStringValue) jpaProperties.get(new TypedStringValue("hibernate.ejb.naming_strategy"));
// only for test!
String newNaming;
if (namingStrategy.getValue().equals(DB1.class.getCanonicalName()))
newNaming = DB2.class.getCanonicalName();
else
newNaming = DB1.class.getCanonicalName();
// only for test!
namingStrategy.setValue(newNaming);
beanFactory.registerBeanDefinition("entityManagerFactory", entityManager);
Thanks,
Regards

Pageable sorting problems with Spring JPA

I'm haccing an issue with Spring JPA regarding sorting and paginating. Currently I'm receiving this warning:
WARN [NamedQuery] (NamedQuery.java:65) - Finder method public abstract org.springframework.data.domain.Page loc.starterkit.business.entities.application.repository.ApplicationRepository.findByNameLike(java.lang.String,org.springframework.data.domain.Pageable)
is backed by a NamedQuery but contains a Pageable parameter! Sorting delivered via this Pageable will not be applied!
My JPA interface code looks like this:
public interface ApplicationRepository extends PagingAndSortingRepository<Application,Long>{
Page<Application> findByNameLike(String name, Pageable pageable);
}
My entity factory configuration:
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="persistenceProviderClass" value="org.hibernate.ejb.HibernatePersistence"/>
<property name="packagesToScan" value="loc.starterkit.business"/>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
<property name="persistenceUnitName" value="general" />
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.connection.charSet">${hibernate.connection.charSet}</prop>
<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
<prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
<prop key="hibernate.use_sql_comments">${hibernate.use_sql_comments}</prop>
<prop key="hibernate.jdbc.batch_size">${hibernate.jdbc.batch_size}</prop>
<prop key="hibernate.cache.use_query_cache">${hibernate.cache.use_query_cache}</prop>
<prop key="hibernate.cache.region.factory_class">${hibernate.cache.region.factory_class}</prop>
</props>
</property>
</bean>
I don't know why I cannot use sorting in pageable methods in this case . Any ideas?
EDIT:
My Application Entity
#Entity
#Table(name="APPLICATION")
public class Application {
#Id
#Column(name="ID")
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#Column(name="NAME")
private String name;
#Column(name="DESCRIPTION")
private String description;
public Application() {
super();
}
.... (getters and setters here)
}
Changing PagingAndSortingRepository to JPARepository seems to throw same warning. BUT, after some tests, I've found out that this WARN means nothing. Things seems to work nicely:
Hibernate:
/* select
count(*)
from
Application as generatedAlias0
where
generatedAlias0.name like :param0 */ select
count(*) as col_0_0_
from
APPLICATION applicatio0_
where
applicatio0_.NAME like ?
Hibernate:
/* select
generatedAlias0
from
Application as generatedAlias0
where
generatedAlias0.name like :param0
order by
generatedAlias0.name asc */ select
applicatio0_.ID as ID0_,
applicatio0_.DESCRIPTION as DESCRIPT2_0_,
applicatio0_.NAME as NAME0_
from
APPLICATION applicatio0_
where
applicatio0_.NAME like ?
order by
applicatio0_.NAME asc limit ?
So I think that this WARN means nothing but I want to know why it's being issued

Hibernate Interceptor Not Working

I have a simple HibernateInterceptor in which basically I want to set a few fields automatically. This interceptor (shown below) extends EmptyInterceptor:
public class EntityAuditInterceptor extends EmptyInterceptor {
/**
* The Serial Version UUID.
*/
private static final long serialVersionUID = 4636626262147801287L;
/* (non-Javadoc)
* #see org.hibernate.EmptyInterceptor#onFlushDirty(java.lang.Object, java.io.Serializable, java.lang.Object[], java.lang.Object[], java.lang.String[], org.hibernate.type.Type[])
*/
public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types) {
// doing stuff here
return false;
}
/* (non-Javadoc)
* #see org.hibernate.EmptyInterceptor#onSave(java.lang.Object, java.io.Serializable, java.lang.Object[], java.lang.String[], org.hibernate.type.Type[])
*/
public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
// doing stuff here
return false;
}
}
I am wiring using a spring config file as follows:
<!-- Hibernate SessionFactory -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="hsqlDbDataSource"/>
<property name="packagesToScan">
<list>
<value>com.dreamteam.lms.**.*</value>
</list>
</property>
<!-- Adding Interceptor here -->
<property name="entityInterceptor">
<bean class="com.dreamteam.lms.interceptors.EntityAuditInterceptor"></bean>
</property>
<property name="hibernateProperties">
<props>
<!--<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>-->
<prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop>
<prop key="hibernate.generate_statistics">true</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.cache.use_second_level_cache">true</prop>
<prop key="hibernate.hbm2ddl.auto">create</prop>
<prop key="hibernate.cache.use_query_cache">true</prop>
<prop key="hibernate.cache.provider_class">net.sf.ehcache.hibernate.SingletonEhCacheProvider</prop>
</props>
</property>
</bean>
However, the Interceptor is never reached. Does anyone have any clue? I have also tried adding the following to the transaction manager bean definition as follows:
<property name="entityInterceptor">
<ref local="entityAuditInterceptor"/>
</property>
Ok, just for the record, I solved this problem which turned out to be a stupid mistake from my part.
When I implemented my interceptor that extends EmptyInterceptor I added the method 'onFlushDirty' and so on. So far so good. The problem was that on using my IDE to auto-import the used classes, I ended up importing java.reflect.Type instead of org.hibernate.type.Type by mistake. Hence, I was not really overriding the interceptor method!
I noticed that when I added the #Override interceptor to my method.
Another mystery solved ... :)
It looks like your Spring XML configuration is correct, so Hibernate should be calling your interceptor methods. However those methods appear to always return false which means that Hibernate will ignore any changes you make.
When you change a value you must return true. For example, this code iterates through all the properties and returns true if and only if a change is made:
public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types) {
boolean changed = false;
for (int i = 0; i < propertyNames.length; i++) {
if ("saveDate".equals(propertyNames[i])) {
currentState[i] = new Date();
changed = true;
}
}
return changed;
}

Resources