Going further into Hibernate i've stumbled upon the following situation: I have an @Entity Person
@Entity
@Table(name = "PERSON")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="type",discriminatorType=DiscriminatorType.STRING)
@DiscriminatorValue(value="person")
public class Person implements PersonInterface{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "USER_ID")
protected Integer id;
@Column(name = "NAME")
protected String name;
@ElementCollection
@Formula("(SELECT groupId FROM PERSON_GROUPS WHERE id = id")
protected List<Integer> groups = new ArrayList<>();
@Override
public Integer getId() {
return id;
}
@Override
public Collection<Integer> getGroups(){
return groups;
}
@Override
public String getName() {
return name;
}
}
Which is split in two parts, Immutable and Mutable:
@Entity
@DiscriminatorValue("mutableperson")
@Transactional
public class MutablePerson extends Person implements MutablePersonInterface{
@Override
public void setName(String name) {
this.name = name;
}
@Override
public void setId(Integer id) {
this.id = id;
}
@Override
public void setGroups(List<Integer> groupIds) {
this.groups = groupIds;
}
public void excludeFromGroup(Integer groupId){
Hibernate.initialize(this.groups);
this.groups.remove(groupId);
}
}
Now, when i try to call something like:
Session session = sessionFactory.openSession();
MutablePerson personFromDb = (MutablePerson) personService.getMutablePersonById(0);
personFromDb.excludeFromGroup(group1.getId());
session.saveOrUpdate(personFromDb);
I get an exception:
Exception in thread "main" org.hibernate.HibernateException: collection is not associated with any session
at org.hibernate.collection.internal.AbstractPersistentCollection.forceInitialization(AbstractPersistentCollection.java:704)
at org.hibernate.Hibernate.initialize(Hibernate.java:65)
at com.studytrails.tutorials.springhibernatejpa.MutablePerson.excludeFromGroup(MutablePerson.java:57)
at com.studytrails.tutorials.springhibernatejpa.TestSpringHibernateJpa.main(TestSpringHibernateJpa.java:48)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Which tells me, that @Transactional annotation for that MutablePerson
class doesn't really work and Hibernate is not creating a transaction.
Here's this bit of my spring configuration:
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
<property name="dataSource" ref="dataSource" />
<property name="jpaDialect" ref="jpaDialect" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
As far as i can tell from Hibernate docs and examples, this config is correct and it should work, but it doesn't. Am i missing yet another Hibernate nuance here?
P.S. I know i could've done this in DAO or Service class, but unfortunately i'm bound to use existing API, which works with groups in the mutable half of the entity.
Entities are not managed by Spring. they are created as a product of database operation via hibernate or manually to save date. therefore the @Transactional annotation will not work as this is not a spring managed bean.
Spring transactional context wraps around a spring managed bean and act on it. it cannot wrap non spring beans
Possible Solution Move the transactional context ideally for service layer or at least Jpa Repository/Dao layer.