hey,
I'm getting quite a headache from this error I currently have:
It appears when trying to update a value of a many-to-many relationship.
I use the Spring-framework to generate my DAO-connection. I have tried several things found on this forum & google, but none worked.
It would mean a lot to me if someone could make some suggestions. Just ask if other code is needed.
included code:
- JUnit testprogram trying the code
- DAO
- Spring & properties
- assigned variable (relationship object)
- assigned variable mapping
- task (one side of the relationship)
- task mapping file
thanks in advance,
Sven
Code:
org.springframework.orm.hibernate3.HibernateSystemException: Illegal attempt to associate a collection with two open sessions; nested exception is org.hibernate.HibernateException: Illegal attempt to associate a collection with two open sessions
Caused by: org.hibernate.HibernateException: Illegal attempt to associate a collection with two open sessions
at org.hibernate.collection.AbstractPersistentCollection.setCurrentSession(AbstractPersistentCollection.java:410)
at org.hibernate.event.def.OnUpdateVisitor.processCollection(OnUpdateVisitor.java:43)
at org.hibernate.event.def.AbstractVisitor.processValue(AbstractVisitor.java:101)
at org.hibernate.event.def.AbstractVisitor.processValue(AbstractVisitor.java:61)
at org.hibernate.event.def.AbstractVisitor.processEntityPropertyValues(AbstractVisitor.java:55)
at org.hibernate.event.def.AbstractVisitor.process(AbstractVisitor.java:123)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performUpdate(DefaultSaveOrUpdateEventListener.java:293)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsDetached(DefaultSaveOrUpdateEventListener.java:223)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:89)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:70)
at org.hibernate.impl.SessionImpl.fireSaveOrUpdate(SessionImpl.java:507)
at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:499)
at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:495)
at org.springframework.orm.hibernate3.HibernateTemplate$16.doInHibernate(HibernateTemplate.java:684)
at org.springframework.orm.hibernate3.HibernateTemplate.execute(HibernateTemplate.java:367)
at org.springframework.orm.hibernate3.HibernateTemplate.saveOrUpdate(HibernateTemplate.java:681)
at dao.ObjectDAO.saveOrUpdateObject(ObjectDAO.java:40)
at sun.reflect.GeneratedMethodAccessor10.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:299)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:196)
at $Proxy1.saveOrUpdateObject(Unknown Source)
at services.TaskServices.variableUpdated(TaskServices.java:136)
at domain.VariableAssigned.notifyListeners(VariableAssigned.java:113)
at domain.VariableAssigned.updateNotification(VariableAssigned.java:95)
at opc.OpcItemWrapped.notifyListener(OpcItemWrapped.java:101)
at opc.OpcItemWrapped.setItem(OpcItemWrapped.java:77)
at opc.OpcGroupMonitored.getAsynchEvent(OpcGroupMonitored.java:212)
at javafish.clients.opc.JOpc.sendOpcGroup(JOpc.java:223)
at javafish.clients.opc.JEasyOpc.run(JEasyOpc.java:90)
at java.lang.Thread.run(Unknown Source)
JUnit testprogram:
Code:
package tests.test05_opcitems;
import services.IOpcServices;
import services.ITaskServices;
import services.IVariableServices;
import tests.TestCaseExtension;
import util.LocaleUtil;
import domain.Task;
import domain.TaskState;
import domain.Variable;
import domain.VariableAssigned;
import events.VariableAssignedListener;
import exceptions.OpcConcurrencyException;
/**
* This testcase will do different tests on OPC-items. It will try to read items
* from a specific OPC-server & then update the item's values.
* It requires no user input.
*
* @author Mathieu Schaessens
* @author Sven Vlieghe
*/
public class TestCase_OpcItem extends TestCaseExtension
implements VariableAssignedListener {
/* -- Class Variables ------------------------------------------------------ */
public IOpcServices opcServices;
public IVariableServices variableServices;
public ITaskServices taskServices;
/* -- Testcase ------------------------------------------------------------- */
public void testOpcItems() {
System.out.println("\n-------- EXECUTING OPC-ITEM TEST -------");
/* set some test locations using the local OPC simulation server */
String locRandom =
"localhost//SWToolbox.TOPServer/Channel_0_User_Defined.Random.Random1";
/* create variables using the test locations */
Variable varRandom = variableServices.createInputVariable();
variableServices.updateVariable(varRandom, "label", "testVar1");
variableServices.updateVariable(varRandom, "opcItemLocation",
locRandom);
/* create assigned variables using these test variables */
Task task = taskServices.createTask();
VariableAssigned varAssignedRandom =
taskServices.assignInputVariable(task, varRandom, "test");
/* start listening for updates of assigned variables */
varAssignedRandom.addListener(this);
/* start monitoring the OPC-items for some seconds */
try {
opcServices.monitorVariable(varAssignedRandom);
/* this will call the updateNotification(String value, Timestamp timestamp,
Boolean quality)-method in AssignedVariable
* that method will notify its listeners
* one of those listeners will call the dao.saveOrUpdate(VariableAssigned var)-method
* crash happens there
*/
}
catch (OpcConcurrencyException e) {
System.err.println(e.getMessage());
}
/* sleep a while to allow outputs of the monitoring process */
try {
Thread.sleep(4000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
opcServices.unmonitorVariable(varAssignedRandom);
}
/* -- Observer For VariableAssigned-Updates -------------------------------- */
public void variableUpdated(VariableAssigned variable) {
System.out.println("New value for " + variable.getProperty("label") +
": " + variable.getValue() + " at time " +
LocaleUtil.formatTime(variable.getTimestamp()));
}
/* -- Automatic Dependency Injection --------------------------------------- */
public void setOpcServices(IOpcServices opcServices) {
this.opcServices = opcServices;
}
public void setVariableServices(IVariableServices variableServices) {
this.variableServices = variableServices;
}
public void setTaskServices(ITaskServices taskServices) {
this.taskServices = taskServices;
}
}
DAO:
Code:
package dao;
import java.io.Serializable;
import java.util.List;
import org.hibernate.Hibernate;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
/**
* This class is an implementation of the DAO-interface (IObjectDAO). It takes
* care of all database activities, using the Hibernate technology. It also extends
* Spring's HibernateDaoSupport-class which automates lots of Hibernate's features.
*
* @author Mathieu Schaessens
* @author Sven Vlieghe
*/
public class ObjectDAO extends HibernateDaoSupport implements IObjectDAO {
/* -- DAO Lookup Methods --------------------------------------------------- */
public List findObjectsOfClass(Class objectClass) {
try {
return getHibernateTemplate()
.findByExample(objectClass.newInstance());
}
catch (Exception e) {
return null;
}
}
public Object findObject(Class objectClass, Serializable id) {
return getHibernateTemplate().get(objectClass, id);
}
/* -- DAO CRUD Methods ----------------------------------------------------- */
public void saveOrUpdateObject(Object object) {
getHibernateTemplate().saveOrUpdate(object);
getHibernateTemplate().flush();
}
public void deleteObject(Object object) {
getHibernateTemplate().delete(object);
getHibernateTemplate().flush();
}
}
Spring-config:
Code:
<!-- DAO References -->
<bean id="objectDAO" class=
"org.springframework.aop.framework.ProxyFactoryBean">
<property name="target">
<bean id="objectDAOInner" singleton="true" class="${class.objectDAO}">
<property name="sessionFactory">
<ref local="sessionFactory"/>
</property>
</bean>
</property>
<property name="proxyInterfaces">
<list>
<value>${class.objectDAOInterface}</value>
</list>
</property>
</bean>
<!-- Transaction Management & Database Access -->
<bean id="transactionBean" abstract="true" class=
"org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager" ref="transactionManager"/>
<!-- dont define a target this time, this is done in the child bean -->
<property name="transactionAttributes">
<props>
<prop key="create*">${tx.create}</prop>
<prop key="update*">${tx.update}</prop>
<prop key="*Updated">${tx.updated}</prop>
<prop key="delete*">${tx.delete}</prop>
<prop key="assign*">${tx.assign}</prop>
<prop key="find*">${tx.find}</prop>
<prop key="list*">${tx.list}</prop>
<prop key="*">${tx.default}</prop>
</props>
</property>
</bean>
<bean id="transactionManager" class=
"org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory">
<ref bean="sessionFactory"/>
</property>
</bean>
<bean id="sessionFactory" class=
"org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
<ref local="dataSource"/>
</property>
<property name="hibernateProperties">
<ref bean="hibernateProperties" />
</property>
<property name="mappingResources">
<value>${hibernate.mapping}</value>
</property>
</bean>
<bean id="dataSource" class="com.mysql.jdbc.jdbc2.optional.MysqlDataSource">
<property name="url">
<value>${db.location}</value>
</property>
<property name="user">
<value>${db.user}</value>
</property>
<property name="password">
<value>${db.password}</value>
</property>
</bean>
<bean id="hibernateProperties" class=
"org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="properties">
<props>
<prop key="hibernate.hbm2ddl.auto">${db.hbm2dll}</prop>
<prop key="hibernate.dialect">${db.dialect}</prop>
<prop key="hibernate.query.substitutions">
${db.substitutions}</prop>
<prop key="hibernate.show_sql">${db.showSql}</prop>
<prop key="hibernate.c3p0.minPoolSize">${db.minPoolSize}</prop>
<prop key="hibernate.c3p0.maxPoolSize">${db.maxPoolSize}</prop>
<prop key="hibernate.c3p0.timeout">${db.timeout}</prop>
<prop key="hibernate.c3p0.max_statement">${db.maxStatement}</prop>
<prop key="hibernate.c3p0.testConnectionOnCheckout">
${db.testConnection}</prop>
</props>
</property>
</bean>
Spring properties:
Code:
# Transaction Management
tx.create=PROPAGATION_REQUIRED
tx.update=PROPAGATION_REQUIRED
tx.updated=PROPAGATION_REQUIRED
tx.delete=PROPAGATION_REQUIRED
tx.assign=PROPAGATION_REQUIRED
tx.find=PROPAGATION_SUPPORTS,readOnly
tx.list=PROPAGATION_SUPPORTS,readOnly
tx.default=PROPAGATION_SUPPORTS,readOnly
# Data Source Configuration
db.hbm2dll=update
db.dialect=org.hibernate.dialect.MySQLDialect
db.substitutions=true 'T', false 'F'
db.showSql=false
db.minPoolSize=5
db.maxPoolSize=20
db.timeout=600
db.maxStatement=50
db.testConnection=false
Assigned Variable (object that represents the relationship):
Code:
package domain;
import java.io.Serializable;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import events.VariableAssignedListener;
/**
* This class represents a relationship between a task-object and a variable-
* object. It is abstract, so a specific implementation of subclasses is required.
* These subclasses will have their own Hibernate mapping, linking them to the
* database.
*
* @author Mathieu Schaessens
* @author Sven Vlieghe
*/
public abstract class VariableAssigned implements Serializable {
/* -- Class Variables ------------------------------------------------------ */
private Task task;
private Variable variable;
private String value;
private Timestamp timestamp;
private Boolean quality;
private List<VariableAssignedListener> listeners
= new ArrayList<VariableAssignedListener>();
/* -- Constructors --------------------------------------------------------- */
public VariableAssigned() {}
/* -- Getters -------------------------------------------------------------- */
public Task getTask() {
return task;
}
public Variable getVariable() {
return variable;
}
public String getValue() {
return value;
}
public Timestamp getTimestamp() {
return timestamp;
}
public Boolean getQuality() {
return quality;
}
/* -- Setters -------------------------------------------------------------- */
public void setTask(Task task) {
this.task = task;
}
public void setVariable(Variable variable) {
this.variable = variable;
}
public void setValue(String value) {
this.value = value;
}
public void setTimestamp(Timestamp timestamp) {
this.timestamp = timestamp;
}
public void setQuality(Boolean quality) {
this.quality = quality;
}
/* -- Update Notification From Observed OPC-Item --------------------------- */
public void updateNotification(String value, Timestamp timestamp,
Boolean quality) {
/* update all values */
setValue(value);
setTimestamp(timestamp);
setQuality(quality);
/* notify the listeners */
notifyListeners();
}
/* -- CRUD Methods For Listeners ------------------------------------------- */
public void addListener(VariableAssignedListener variableAssignedListener) {
listeners.add(variableAssignedListener);
}
public void removeListener(VariableAssignedListener variableAssignedListener) {
listeners.remove(variableAssignedListener);
}
public void notifyListeners() {
for (VariableAssignedListener listener : listeners) {
System.out.println(listener.toString());
listener.variableUpdated(this);
}
}
/* -- Equality Control For Hibernate --------------------------------------- */
public boolean equals(Object otherAV) {
/* basic equality checks */
if (otherAV instanceof VariableAssigned == false)
return false;
if (this == otherAV)
return true;
/* advanced check on ID's */
VariableAssigned otherAVCasted = (VariableAssigned) otherAV;
return new EqualsBuilder()
.append(this.getTask().getId(), otherAVCasted.getTask().getId())
.append(this.getVariable().getId(), otherAVCasted.getVariable().getId())
.isEquals();
}
public int hashCode() {
/* make a temporary hashcode if object isn't saved yet */
if (this.getProperty("id")==null || this.getTask()==null)
return super.hashCode();
/* advanced hashcodebuilder based on ID's */
return new HashCodeBuilder(17, 37)
.append(this.getTask().getId())
.append(this.getVariable().getId())
.toHashCode();
}
}
relationship mapping file:
Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<!-- Mapping Of The Task-VariableIn Relationship-Table -->
<class name="domain.VariableAssignedIn" table="task_variable_in">
<!-- Mapping Of The Task-VariableIn Relationship -->
<composite-id>
<key-many-to-one
name="task"
class="domain.Task"
column="task_id"
/>
<key-many-to-one
name="variable"
class="domain.VariableIn"
column="variable_id"
/>
</composite-id>
<!-- Property Mapping -->
<property name="value"
column="value"
type="string"
length="100"
/>
<property name="timestamp"
column="timestamp"
type="timestamp"
/>
<property name="quality"
column="quality"
type="boolean"
/>
</class>
</hibernate-mapping>
task-class (object having the relationship-mapping):
Code:
package domain;
import java.util.HashSet;
import java.util.Set;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import util.ReflectionUtil;
import util.StringUtil;
/**
* This class represents a task-object. It contains a list of all input and
* output variables, in order to work more efficiently. Hibernate will fetch
* all needed relations (being the variables) of the task automatically.
*
* @author Mathieu Schaessens
* @author Sven Vlieghe
*/
public class Task {
/* -- Class Variables ------------------------------------------------------ */
private Long id;
private TaskState state = TaskState.STATE_INITIALIZING;
private Set<VariableAssigned> inputVariables
= new HashSet<VariableAssigned>();
private Set<VariableAssigned> outputVariables
= new HashSet<VariableAssigned>();
/* -- Constructors --------------------------------------------------------- */
public Task() {}
/* -- Getters -------------------------------------------------------------- */
public Long getId() {
return id;
}
public TaskState getState() {
return state;
}
public Set<VariableAssigned> getInputVariables() {
return inputVariables;
}
public Set<VariableAssigned> getOutputVariables() {
return outputVariables;
}
/* -- Setters -------------------------------------------------------------- */
public void setId(Long id) {
this.id = id;
}
public void setState(TaskState state) {
this.state = state;
}
public void setInputVariables(Set<VariableAssigned> inputVariables) {
this.inputVariables = inputVariables;
}
public void setOutputVariables(Set<VariableAssigned> outputVariables) {
this.outputVariables = outputVariables;
}
/* -- CRUD Methods On Input Collection ------------------------------------- */
public VariableAssigned getInputVariable(Long variableId) {
for( VariableAssigned variable : inputVariables)
if (variable.getProperty("id").equals(variableId))
return variable;
return null;
}
public void addInputVariable(VariableAssigned inputVariable) {
inputVariables.add(inputVariable);
}
public void removeInputVariable(VariableAssigned inputVariable) {
inputVariables.remove(inputVariable);
}
/* -- CRUD Methods On Output Collection ------------------------------------ */
public VariableAssigned getOutputVariable(Long variableId) {
for( VariableAssigned variable : outputVariables)
if (variable.getProperty("id").equals(variableId))
return variable;
return null;
}
public void addOutputVariable(VariableAssigned outputVariable) {
outputVariables.add(outputVariable);
}
public void removeOutputVariable(VariableAssigned outputVariable) {
outputVariables.remove(outputVariable);
}
/* -- Equality Control For Hibernate --------------------------------------- */
public boolean equals(Object otherTask) {
/* basic equality checks */
if (otherTask instanceof Task == false)
return false;
if (this == otherTask)
return true;
/* advanced check on ID */
Task otherTaskCasted = (Task) otherTask;
return new EqualsBuilder()
.append(this.getId(), otherTaskCasted.getId())
.isEquals();
}
public int hashCode() {
/* make a temporary hashcode if object isn't saved yet */
if (this.getId()==null)
return super.hashCode();
/* advanced hashcodebuilder based on ID */
return new HashCodeBuilder(17, 37)
.append(this.getId())
.toHashCode();
}
}
task mapping:
Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<!-- Mapping Of The Task-Table -->
<class name="domain.Task" table="task">
<!-- Key Mapping -->
<id name="id"
column="task_id"
unsaved-value="null">
<generator class="native" />
</id>
<!-- Property Mapping -->
<property name="state"
column="task_state"
type="domain.TaskState"
not-null="true"
/>
<!-- Mapping Of The Task's Relationships -->
<set name="inputVariables" cascade="all" inverse="true">
<key column="task_id"/>
<one-to-many class="domain.VariableAssignedIn"/>
</set>
<set name="outputVariables" cascade="all" inverse="true">
<key column="task_id"/>
<one-to-many class="domain.VariableAssignedOut"/>
</set>
</class>
</hibernate-mapping>