I am using Hibernate 4.3.8.
Oracle Release 12.1.0.2.0 .
I have two entity name as Child and Parent Entity.
They are basically joined entity.
We are using Hibernate batching property for performance reason but when it try to save record in Child entity it throws StackOverflowError.
Hibernate Batching property:
<property name="hibernate.jdbc.batch_size">100</property>
<property name="hibernate.jdbc.batch_versioned_data">true</property>
<property name="hibernate.order_inserts">true</property>
<property name="hibernate.order_updates">true</property>
Child Entity HBM
<hibernate-mapping>
<class entity-name="ChildEntity_Name" table="ChildEntity_DB" dynamic-insert="true" dynamic-update="true">
<id name="ID" type="java.lang.Long" column="ID">
<generator class="native"/>
</id>
<version name="RevisionId" access="field" column="RevisionId" type="java.lang.Long" unsaved-value="undefined" generated="never"/>
<property name="ModifiedUser" type="java.lang.String" column="ModifiedUser"/>
<property name="DealName_db" type="java.lang.String" column="DealName_db"/>
<property name="TotalPrice_db" type="java.lang.Long" column="TotalPrice_db"/>
<property name="DealDate_db" type="java.lang.String" column="DealDate_db"/>
<property name="ModifiedDate" type="java.util.Date" column="ModifiedDate"/>
<property name="CreatedUser" type="java.lang.String" column="CreatedUser"/>
<property name="CreatedDate" type="java.util.Date" column="CreatedDate"/>
<many-to-one name="CE_UK" entity-name="Parent_Entity_Name" property-ref="UK_PD1" unique="false" fetch="join" update="true" insert="true">
<column name="CustID_db" not-null="true"/>
</many-to-one>
<properties name="UK_Child" unique="true" insert="true" update="true">
<property name="DealId_db" type="java.lang.String" column="DealId_db"/>
</properties>
</class>
</hibernate-mapping>
Parent Entity HBM:
<hibernate-mapping>
<class entity-name="Parent_Entity_Name" table="Parent_Entity_DB" dynamic-insert="true" dynamic-update="true" mutable="true" polymorphism="implicit" select-before-update="false" optimistic-lock="version">
<id name="ID" type="java.lang.Long" column="ID">
<generator class="native"/>
</id>
<version name="RevisionId" access="field" column="RevisionId" type="java.lang.Long" unsaved-value="undefined" generated="never"/>
<property name="ModifiedUser" type="java.lang.String" column="ModifiedUser" unique="false" optimistic-lock="true" lazy="false" generated="never"/>
<property name="CustName_db" type="java.lang.String" column="CustName_db" unique="false" optimistic-lock="true" lazy="false" generated="never"/>
<property name="CustAddress_db" type="java.lang.String" column="CustAddress_db" unique="false" optimistic-lock="true" lazy="false" generated="never"/>
<property name="ModifiedDate" type="java.util.Date" column="ModifiedDate" unique="false" optimistic-lock="true" lazy="false" generated="never"/>
<property name="CreatedUser" type="java.lang.String" column="CreatedUser" unique="false" optimistic-lock="true" lazy="false" generated="never"/>
<property name="CreatedDate" type="java.util.Date" column="CreatedDate" unique="false" optimistic-lock="true" lazy="false" generated="never"/>
<properties name="UK_PD1" unique="true" insert="true" update="true" optimistic-lock="true">
<property name="CustID_db" type="java.lang.String" column="CustID_db" unique="false" optimistic-lock="true" lazy="false" generated="never"/>
</properties>
</class>
</hibernate-mapping>
Class:
import java.util.HashMap;
import java.util.Map;
import org.hibernate.Hibernate;
import org.hibernate.Session;
import org.hibernate.Transaction;
public class TestBatching {
public static void main(String[] args) {
Session session = null;
Transaction tx = null;
try {
session = HibernateUtil.getSessionFactory().openSession();
System.out.println("Got the hibernate session ...");
tx = session.beginTransaction();
System.out.println("Opening tx ...");
Map<String, Object> parentEntityMap = new HashMap();
HashMap uniqueKeyMapping = new java.util.HashMap<>();
uniqueKeyMapping.put("CustID_db", String.valueOf(1));
parentEntityMap.put("CustID_db", String.valueOf(1));
parentEntityMap.put("UK_PD1", uniqueKeyMapping);
parentEntityMap.put("CustName_db", String.valueOf("John"));
parentEntityMap.put("CustAddress_db", String.valueOf("Mumbai"));
parentEntityMap.put("ModifiedDate", java.sql.Timestamp.valueOf("2017-11-15 15:30:14.332"));
parentEntityMap.put("CreatedDate", java.sql.Timestamp.valueOf("2017-11-15 15:30:14.332"));
parentEntityMap.put("ModifiedUser", String.valueOf("User1"));
parentEntityMap.put("CreatedUser", String.valueOf("User1"));
System.out.println("Saving record in Parent Entity ......");
session.save("Parent_Entity_Name", parentEntityMap);
System.out.println("Saved record in Parent Entity ......");
Map<String, Object> childEntityMap = new HashMap();
HashMap uniqueKeyMapping1 = new java.util.HashMap<>();
uniqueKeyMapping1.put("DealId_db", String.valueOf(1));
childEntityMap.put("UK_Child", uniqueKeyMapping1);
childEntityMap.put("DealId_db", String.valueOf(1));
childEntityMap.put("CustID_db", String.valueOf(1));
childEntityMap.put("DealName_db", String.valueOf("SS"));
childEntityMap.put("DealDate_db", "2017-11-15 15:30:14.332");
childEntityMap.put("TotalPrice_db", Long.valueOf(100));
childEntityMap.put("RevisionId", Long.valueOf("1"));
HashMap fkMapping = new java.util.HashMap<>();
fkMapping.put("CustID_db", String.valueOf(1));
fkMapping.put("ID", Long.valueOf("1"));
fkMapping.put("UK_PD1", fkMapping);
fkMapping.put("RevisionId", Long.valueOf("1"));
childEntityMap.put("CE_UK", fkMapping);
childEntityMap.put("ModifiedDate", java.sql.Timestamp.valueOf("2017-11-15 15:30:14.332"));
childEntityMap.put("CreatedDate", java.sql.Timestamp.valueOf("2017-11-15 15:30:14.332"));
childEntityMap.put("ModifiedUser", String.valueOf("User1"));
childEntityMap.put("CreatedUser", String.valueOf("User1"));
System.out.println("Saving record in Child Entity ......");
session.save("ChildEntity_Name", childEntityMap);
System.out.println("Saved record in Child Entity ......");
System.out.println("Flushing session ......");
session.flush();
System.out.println("Flushed session ......");
System.out.println("Commiting tx ......");
tx.commit();
System.out.println("Commited tx ......");
} catch (Exception e) {
e.printStackTrace();
} finally {
if (session != null) {
session.close();
System.out.println(" closing hibernate session ...");
}
}
}
}
Hibernate Configuration:
<hibernate-configuration>
<session-factory>
<property name="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</property>
<property name="hibernate.connection.driver_class">oracle.jdbc.OracleDriver</property>
<property name="hibernate.connection.url">jdbc:oracle:thin:#localhost:1521:xe</property>
<property name="hibernate.connection.username">user1</property>
<property name="hibernate.connection.password">user1</property>
<!-- Hibernate caching settings -->
<property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
<property name="hibernate.cache.use_second_level_cache">false</property>
<!-- Hibernate-c3p0 settings -->
<property name="hibernate.c3p0.min_size">2</property>
<property name="hibernate.c3p0.max_size">50</property>
<property name="hibernate.c3p0.acquire_increment">1</property>
<property name="hibernate.c3p0.idle_test_period">100</property>
<property name="hibernate.c3p0.timeout">100</property>
<property name="hibernate.c3p0.max_statements">0</property>
<!-- Hibernate-Batching settings -->
<property name="hibernate.jdbc.batch_size">100</property>
<property name="hibernate.jdbc.batch_versioned_data">true</property>
<property name="hibernate.order_inserts">true</property>
<property name="hibernate.order_updates">true</property>
<!-- HBM Mapping files -->
<mapping resource="Child_entity.hbm.xml"/>
<mapping resource="Parent_entity.hbm.xml"/>
</session-factory>
</hibernate-configuration>
Output:
Got the hibernate session ...
Opening tx ...
Saving record in Parent Entity ......
Saved record in Parent Entity ......
Saving record in Child Entity ......
Saved record in Child Entity ......
Flushing session ......
closing hibernate session ...
Exception in thread "main" java.lang.StackOverflowError
at java.base/java.lang.Long.hashCode(Long.java:1402)
at java.base/java.util.Objects.hashCode(Objects.java:116)
at java.base/java.util.HashMap$Node.hashCode(HashMap.java:297)
at java.base/java.util.AbstractMap.hashCode(AbstractMap.java:527)
at java.base/java.util.Objects.hashCode(Objects.java:116)
Note: Without hibernate batching property it is working fine.
Can anyone please help me here?
Related
We are using hibernate to populate a ddbb with data from multiples files. Multiples threads are proccessing the files and when x files are processed we flush the data on the database.
When i try to write on the database we get the next error:
core.exception.SavingException: org.hibernate.MappingException: Unknown entity: FilesPT
at databaseaccess.session.GenericManagerImpl.insertBatch(GenericManagerImpl.java:75)
at FileWriter.flushFiles(FileWriter.java:425)
Caused by: org.hibernate.MappingException: Unknown entity: FilesPT
at org.hibernate.impl.SessionFactoryImpl.getEntityPersister(SessionFactoryImpl.java:597)
at org.hibernate.impl.StatelessSessionImpl.getEntityPersister(StatelessSessionImpl.java:376)
at org.hibernate.impl.StatelessSessionImpl.insert(StatelessSessionImpl.java:80)
at databaseaccess.dao.GenericDAOImpl.insertList(GenericDAOImpl.java:36)
at session.GenericManagerImpl.insertBatch(GenericManagerImpl.java:72)
... 4 more
My Hibernate config file is:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"classpath://org/hibernate/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
<property name="hibernate.connection.url">jdbc:oracle:thin:#%DB_HOST%:%DB_PORT%:%DB_SID%</property>
<property name="hibernate.connection.username">%DB_USER%</property>
<property name="hibernate.connection.password">%DB_PASS%</property>
<property name="hibernate.dialect">org.hibernate.dialect.OracleDialect</property>
<property name="hibernate.default_schema">%DB_SCHEMA%</property>
<property name="hibernate.c3p0.min_size">5</property>
<property name="hibernate.c3p0.max_size">20</property>
<property name="hibernate.c3p0.timeout">300</property>
<property name="hibernate.c3p0.max_statements">50</property>
<property name="hibernate.c3p0.idle_test_period">3000</property>
<property name="hibernate.show_sql">false</property>
<property name="hibernate.format_sql">false</property>
<!-- Enable Hibernate's automatic session context management -->
<property name="current_session_context_class">thread</property>
<mapping resource="databaseaccess/entities/Files.hbm.xml"/>
</session-factory>
</hibernate-configuration>
The file databaseaccess/entities/Files.hbm.xml has two different entities:
<hibernate-mapping>
<class name="databaseaccess.entities.Files" table="FILES_PT" entity-name="FilesPT">
<id name="dealId" column="FILE_ID" type="java.lang.String" length="50" />
<property name="filename" column="FILENAME" type="java.lang.String" length="250" />
<property name="folder" column="FOLDER" type="java.lang.String" length="30" />
</class>
<class name="databaseaccess.entities.Files" table="FILES_ES" entity-name="FilesES">
<id name="dealId" column="FILE_ID" type="java.lang.String" length="50" />
<property name="filename" column="FILENAME" type="java.lang.String" length="250" />
<property name="folder" column="FOLDER" type="java.lang.String" length="30" />
</class>
</hibernate-mapping>
To save the data we are using an Stateless session generated from the hibernateUtil:
public void insertList(final String entityName, final Collection entities) {
StatelessSession session = HibernateUtil.openStatelessSession();
try {
Transaction tx = session.beginTransaction();
try {
for (Object entity : entities) {
session.insert(entityName, entity);
}
tx.commit();
} catch (final HibernateException e) {
logger.error("Error inserting " + entities.size() + " rows in database. " + e.getMessage() + ":\n "
+ e.getMessage());
throw e;
} finally {
tx.rollback();
}
} finally {
session.close();
}
}
The hibernate util generate a static session factory
static {
try {
// Create the SessionFactory from config file.
File fileCfgHibernate = new File(ConfigurationManager.getProperty("Global.properties", "HIBERNATE_CFG_FILE"));
logger.info("Opening DB session with configuration from: " + fileCfgHibernate.getPath());
Configuration cfgHibernate = new Configuration().configure(fileCfgHibernate);
sessionFactory = cfgHibernate.buildSessionFactory();
} catch (Throwable ex) {
// Log the exception.
logger.error("DB session creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static StatelessSession openStatelessSession() {
return sessionFactory.openStatelessSession();
}
We have checked that the session factory use the configuration on the hibernate.cfg file but it seems that the session is not getting the configuration. What are we missing?
I want to confirm before going my code to production.
I am using LinkedBlockingQueue like shown below.
I have one producer thread and five consumer thread.
Consumer thread does DB operation, So I have increased it to 5 to fasten the process.
Now, is there any way I will run into (Synchronization/Multiple updates for row/or any issue which I need to take care) problem either at Code or DB level.
Below is my complete code.
private int numberOfConsumers = 5;
private LinkedBlockingQueue<RequestParameters> processorQueue;
public void init(){
processorQueue = new LinkedBlockingQueue<RequestParameters>();
for (int i = 0; i < numberOfConsumers ; i++) {
try {
QueueConsumer c = new QueueConsumer(processorQueue);
c.setName("ThreadName-"+i);
c.start();
} catch (Exception e) {
logger.error("", e);
}
}
this.postCallProcessorDaemon = this;
}
class QueueConsumer extends Thread {
private Logger log = Logger.getLogger(QueueConsumer.class);
private final BlockingQueue<RequestParameters> queue;
QueueConsumer(BlockingQueue<RequestParameters> q) { queue = q; }
public void run() {
try {
while (true) {
RequestParameters rp = queue.take();
consumeRecord(rp);
}
} catch (Exception ex) {
log.error("Exception in run method", ex);
}
}
void consumeRecord(RequestParameters requestParameters)
{
try{
process(requestParameters);
} catch (Throwable e) {
log.error("Exception",e);
}
}
private void process(RequestParameters requestParameters) throws Exception{
for (Action each : allActions) {
if(each.doProcessing(requestParameters)){
boolean status = each.process(requestParameters);
}
}
}
}
public boolean process(RequestParameters parameters) {
//In my process method, I am inserting rows in table based on data in Queue.
}
HBM file which I am using for ORM mapping
<class name="CampaignSubscriptionsSummary" table="campaign_subscriptions_summary">
<composite-id name="id" class="com.on.robd.config.db.reports.CampaignSubscriptionsSummaryId">
<key-property name="reportTime" type="timestamp">
<column name="REPORT_TIME" length="19" />
</key-property>
<key-property name="campaignId" type="long">
<column name="CAMPAIGN_ID" />
</key-property>
<key-property name="recipient" type="string">
<column name="RECIPIENT" length="24" />
</key-property>
<key-property name="selectedPack" type="string">
<column name="SELECTED_PACK" length="256" />
</key-property>
</composite-id>
<many-to-one name="campaign" class="com.on.robd.config.db.campaigns.Campaign" update="false" insert="false" fetch="select">
<column name="CAMPAIGN_ID" not-null="true" />
</many-to-one>
<many-to-one name="campaignSubscriptionsStatusDim" class="com.on.robd.config.db.reports.CampaignSubscriptionStatusDim" fetch="select">
<column name="SUBSCRIPTION_STATUS_ID" not-null="true" />
</many-to-one>
<property name="sender" type="string">
<column name="SENDER" length="24" not-null="true" />
</property>
<property name="callDuration" type="java.lang.Integer">
<column name="CALL_DURATION" />
</property>
<property name="dtmfInput" type="string">
<column name="DTMF_INPUT" length="16" not-null="true" />
</property>
<property name="promptForPack" type="string">
<column name="PROMPT_FOR_PACK" length="256" />
</property>
<property name="wavFile" type="string">
<column name="WAV_FILE" length="256" />
</property>
<property name="subscriptionUrl" type="string">
<column name="SUBSCRIPTION_URL" length="256" />
</property>
<property name="language" type="string">
<column name="LANGUAGE" length="256" />
</property>
<property name="reobdTime" type="timestamp">
<column name="REOBD_TIME" length="19" />
</property>
<many-to-one name="campaignTypeDim" class="com.on.robd.config.db.reports.CampaignTypeDim" fetch="select">
<column name="CAMPAIGN_TYPE_ID" not-null="true" />
</many-to-one>
</class>
Most likely you'll have DB locking issues between the consumers, but it depends on your database vendor. Check your DB vendors docs to see if it uses table, row or partition locking on write.
But Regardless you'll get better write performance is you use batch inserting. See http://docs.jboss.org/hibernate/orm/3.3/reference/en-US/html/batch.html . The simplest way to batch, would be to have your Producer push a collection of RequestParameters into your BlockingQueue. This could also reduce the number of open connections you have to the DB which will also help with performance.
I have a query which when i run in sqlDeveloper i get the result. Here is the query
select * from Losa_Cust_Reject_App rejectApp
inner join
Losa_Cust losaCust
on
rejectApp.app_Ref_No = losaCust.app_Ref_No
where
rejectApp.app_Ref_No != '0000001912' and rejectApp.app_Dt >= '23-SEP-2012'
and
rejectApp.cust_Id1 = 'A111111111' and rejectApp.cust_Id1_Type = '01';
i translated this query to hibernate as following
public List findAppByDate(String appRefNo, Date previousAppDate, String currentId1,
String currentIdType1) {
StringBuffer query = new StringBuffer("from ");
query.append(LosaCustRejectApp.class.getName());
query.append(" rejectApp inner join ");
query.append(LosaCust.class.getName());
query.append(" losaCust with rejectApp.appRefNo = losaCust.comp_id.appRefNo");
query.append(" where rejectApp.appRefNo != ? and rejectApp.appDt >= ?");
query.append(" and rejectApp.custId1 = ? and rejectApp.custId1Type = ? ");
List result = null;
try {
result = getHibernateTemplate().find(query.toString(),
new Object[] { appRefNo, previousAppDate, currentId1, currentIdType1 });
if (CollectionUtils.isNotEmpty(result)) {
return result;
}
} catch (Exception e) {
String message = e.getMessage();
System.out.println();
}
return result;
}
But i want to define mapping in hibernate so hibernate make the join automatically. How can i define association in hibernate file for the above query so hibernate make the join automatically.
here is my LosaCustReject.hbm.xml
<hibernate-mapping default-lazy="false">
<class name="com.thetasp.losa.data.LosaCustRejectApp" table="LOSA_CUST_REJECT_APP" optimistic-lock="version">
<id name="rejectAppId" type="java.lang.Long" column="REJECT_APP_ID">
<generator class="com.thetasp.code.runningno.HibernateId">
<param name="fieldCode">REJECT_APP_ID</param>
<param name="returnType">java.lang.Long</param>
</generator>
</id>
<version name="rowVersion" type="int" column="ROW_VERSION" access="property" unsaved-value="undefined" />
<property name="appRefNo" type="java.lang.String" column="APP_REF_NO" not-null="true" length="20" />
<property name="appDt" type="java.sql.Timestamp" column="APP_DT" not-null="true" length="23" />
<property name="custId1Type" type="java.lang.String" column="CUST_ID1_TYPE" not-null="true" length="1" />
<property name="custId1" type="java.lang.String" column="CUST_ID1" not-null="true" length="20" />
.......
<property name="updateDt" type="java.sql.Timestamp" column="UPDATE_DT" length="23" />
<!-- associations -->
</class>
</hibernate-mapping>
LosaCust.hbm.xml
<hibernate-mapping default-lazy="false">
<class name="com.thetasp.losa.data.LosaCust" table="LOSA_CUST" discriminator-value="I" optimistic-lock="version">
<composite-id name="comp_id" class="com.thetasp.losa.data.LosaCustPK">
<key-property name="custId" column="CUST_ID" type="java.lang.String" length="20" />
<key-property name="appRefNo" column="APP_REF_NO" type="java.lang.String" length="20" />
</composite-id>
<discriminator column="CUST_T" type="string" length="1" >
</discriminator>
<version name="rowVersion" type="int" column="ROW_VERSION" access="property" unsaved-value="undefined" />
<property name="custLevel" type="java.lang.String" column="CUST_LEVEL" not-null="false" length="10" />
.....
<property name="staySinceMth" type="java.lang.String" column="STAYED_SINCE_MTH" length="10" />
....
<subclass name="com.thetasp.losa.data.LosaIndvCust" discriminator-value="I" lazy="false" >
<property name="id1Type" type="java.lang.String" column="ID1_TYPE" length="1" />
<property name="id1" type="java.lang.String" column="ID1" length="20" />
....
</subclass>
</class>
Thanks
I think you could do something like this
In LosaCust.java define a field representing the one-to-many association to LosaCustRejectApp entities:
List<LosaCustRejectApp> associatedLosaCustRejectApps;
In LosaCust.hbm.xml you map this new field using something like this:
<list name="associatedLosaCustRejectApps" table="LOSA_CUST_REJECT_APP" >
<key>
<column name="app_Ref_No" not-null="true" />
</key>
<one-to-many class="com.thetasp.losa.data.LosaCustRejectApp" />
</set>
For more details and even a complete tutorial on how to do this type of mapping you can go here:
Hibernate – One-to-Many example (XML Mapping)
Best regards
I am newbie in hybernate and I am struggling with the following exception:
Exception in thread "AWT-EventQueue-0"
org.hibernate.HibernateException: illegally attempted to associate a
proxy with two open Sessions
I get this when I try to delete an object (an order).
My setting/code:
Order.hbm.xml
<hibernate-mapping>
<class name="com.database.entities.Order" table="ORDERS">
<id name="orderId" type="java.lang.Long">
<column name="ORDERID" />
<generator class="identity" />
</id>
<property name="product" type="java.lang.String">
<column name="SHIP" />
</property>
<property name="orderDate" type="java.util.Date">
<column name="ORDERDATE" />
</property>
<property name="quantity" type="java.lang.Integer">
<column name="QUANTITY" />
</property>
<many-to-one name="customer" class="com.database.entities.Customer" fetch="join">
<column name="CUSTOMERID"/>
</many-to-one>
<many-to-one name="associate" column="ASSOCIATEID" class="com.database.entities.Employee" fetch="join">
</many-to-one>
</class>
</hibernate-mapping>
A session holder:
public class DBSession {
private static SessionFactory sessionFactory;
static{
Configuration cfg = new Configuration();
sessionFactory = cfg.configure().buildSessionFactory();
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
public static void shutdown() {
getSessionFactory().close();
}
}
And relevant DAOs all extending the following:
public abstract class GenericDAO<T, ID extends Serializable> implements GenericDAOIF<T, ID> {
private Class<T> persistentClass;
private Session session;
public Session getSession() {
if(session == null || !session.isOpen()){
session = DBUtil.getSessionFactory().openSession();
}
return session;
}
public void setSession(Session session) {
this.session = session;
}
#SuppressWarnings("unchecked")
#Override
public T findById(ID id, boolean lock) {
T entity;
if(lock)
entity = (T) getSession().load(getPersistentClass(), id, LockMode.UPGRADE);
else
entity = (T) getSession().load(getPersistentClass(), id);
return entity;
}
#Override
public T makePersistent(T entity) {
getSession().beginTransaction();
getSession().saveOrUpdate(entity);
flush();
getSession().getTransaction().commit();
return entity;
}
#Override
public void flush() {
getSession().flush();
}
#Override
public void clear() {
getSession().clear();
}
#Override
public void makeTransient(T entity) {
getSession().getTransaction().begin();
getSession().delete(entity);
getSession().getTransaction().commit();
}
}
While all the other queries work (e.g. insert/select for other entities and order) when I try to delete an order I get the following exception in the following part of the code of GenericDAO:
public void makeTransient(T entity) {
getSession().getTransaction().begin();
getSession().delete(entity);//--> Exception here
getSession().getTransaction().commit();
}
The exception stack trace:
Exception in thread "AWT-EventQueue-0" org.hibernate.HibernateException: illegally attempted to associate a proxy with two open Sessions
at org.hibernate.proxy.AbstractLazyInitializer.setSession(AbstractLazyInitializer.java:126)
at org.hibernate.engine.StatefulPersistenceContext.reassociateProxy(StatefulPersistenceContext.java:573)
at org.hibernate.engine.StatefulPersistenceContext.unproxyAndReassociate(StatefulPersistenceContext.java:618)
at org.hibernate.event.def.DefaultDeleteEventListener.onDelete(DefaultDeleteEventListener.java:89)
at org.hibernate.event.def.DefaultDeleteEventListener.onDelete(DefaultDeleteEventListener.java:73)
at org.hibernate.impl.SessionImpl.fireDelete(SessionImpl.java:956)
at org.hibernate.impl.SessionImpl.delete(SessionImpl.java:934)
at com.dao.GenericDAO.makeTransient(GenericDAO.java:100)
at com.ui.panels.AdminDBPanel$11.actionPerformed(AdminDBPanel.java:414)
at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
This happens only when I delete an order (that is why I posted only that part of code).
I do not understand what this exception means.
But searching Google I tried the following which did not work:
1) Merging with session:
#Override
public void makeTransient(T entity) {
getSession().merge(entity);
getSession().getTransaction().begin();
getSession().delete(entity);
getSession().getTransaction().commit();
}
2) Closing the session and reopening it:
public Session getSession() {
if(session == null || !session.isOpen()){
session = DBUtil.getSessionFactory().openSession();
}
else{
session.close();
session = DBUtil.getSessionFactory().openSession();
}
return session;
}
I am stuck on how to debug this.
Any input is highly welcome
UPDATE:
Extra info:
Employee mapping:
<hibernate-mapping>
<class name="com.database.entities.Employee" table="ASSOCIATE">
<id name="assosiateId" type="java.lang.Long">
<column name="ASSOCIATEID" />
<generator class="identity" />
</id>
<property name="firstName" type="java.lang.String" not-null="true">
<column name="FIRSTNAME" />
</property>
<property name="lastName" type="java.lang.String" not-null="true">
<column name="LASTNAME" />
</property>
<property name="userName" type="java.lang.String" not-null="true">
<column name="USERNAME" />
</property>
<property name="password" type="java.lang.String" not-null="true">
<column name="PASSWORD" />
</property>
<set name="orders" table="ORDERS" inverse="true" cascade="all" lazy="true">
<key>
<column name="ASSOCIATEID" />
</key>
<one-to-many class="com.database.entities.Order" />
</set>
</class>
</hibernate-mapping>
<hibernate-mapping>
<class name="com.database.entities.Customer" table="CUSTOMER">
<id name="customerId" type="java.lang.Long">
<column name="CUSTOMERID" />
<generator class="identity" />
</id>
<property name="customerName" type="java.lang.String">
<column name="CUSTOMERNAME" />
</property>
<set name="orders" table="ORDERS" inverse="true" cascade="all" lazy="true">
<key>
<column name="CUSTOMERID" />
</key>
<one-to-many class="com.database.entities.Order" />
</set>
</class>
</hibernate-mapping>
Customer mapping:
<hibernate-mapping>
<class name="com.database.entities.Customer" table="CUSTOMER">
<id name="customerId" type="java.lang.Long">
<column name="CUSTOMERID" />
<generator class="identity" />
</id>
<property name="customerName" type="java.lang.String">
<column name="CUSTOMERNAME" />
</property>
<set name="orders" table="ORDERS" inverse="true" cascade="all" lazy="true">
<key>
<column name="CUSTOMERID" />
</key>
<one-to-many class="com.database.entities.Order" />
</set>
</class>
</hibernate-mapping>
OrderDAO:
public class OrderDAO extends GenericDAO<Order, Long> implements OrderDAOIF {
#Override
public void addOrder(Order order, Customer customer, Associate associate) {
Session session = getSession();
Transaction tx = session.beginTransaction();
order.setAssociate(associate);
order.setCustomer(customer);
session.saveOrUpdate(order);
tx.commit();
session.close();
}
#Override
public void updateOrderStatus(String status, Long orderId) {
Order order = findById(orderId, false);
order.setOrderState(status);
Session session = getSession();
Transaction tx = session.beginTransaction();
session.saveOrUpdate(order);
tx.commit();
session.close();
}
}
The code that starts the exception:
Order order = getFactory().getOrderDAO().findById(Long.valueOf(orderId), false);
getFactory().getOrderDAO().makeTransient(order);//--> Exception thrown here
The error means that you are trying to associate a object loaded in one session with another session. There is a bigger issue with how you are doing session management - but I cannot comment on that without a lot more info. The merge work around you tried can fix the problem with a simple change - use the reference returned by the merge method for further operation.
#Override
public void makeTransient(T entity) {
T newEntityRef = getSession().merge(entity);
getSession().getTransaction().begin();
getSession().delete(newEntityRef);
getSession().getTransaction().commit();
}
The problem is due to this piece of code.
Order order = getFactory().getOrderDAO().findById(Long.valueOf(orderId), false);
getFactory().getOrderDAO().makeTransient(order);//--> Exception thrown here
Assuming that the getOrderDao() is creating a new instance of the the OrderDao class. The call to findById uses a new instance of session (let us call it s1) to load the object. The loaded object is a proxy which is associated with the session that was used to load it. Then next call creates a new OrderDAO (by calling getOrderDAO method) - now when makeTransient is called a new session (let us call this s2) is created. You are now trying to pass a proxy loaded by s1 to s2 - which is what the exception is indicating. Merge method takes the object and either creates a new object in s2 or merges content with an existing object - either ways the input object passed is not changed - the new object that is created will be the return value.
Writing the code this way will also fix the issue without the merge.
OrderDao orderDao = getFactory().getOrderDAO();
Order order = orderDao.findById(Long.valueOf(orderId), false);
orderDao.makeTransient(order);
The other problem with the above code is that the session is not getting closed. Each session uses a JDBC connection - so it will lead to a connection leak.
Take a look at the following to get an idea of how to fix your code. Basically your DAO should not open a new session and should not be managing transactions. You should be doing it outside.
http://community.jboss.org/wiki/SessionsAndTransactions
http://community.jboss.org/wiki/GenericDataAccessObjects
I have a parent-/child relationship of folders, which looks like this:
A folder can have 0-1 parent folders.
A folder can have 0-n child folders (subfolders).
Using Hibernate, I call session.update(folder) on these folders.
When such a folder has NO subfolders, everything works alright.
BUT when a folder has subfolders, session.update(folder) will make the subfolder(s) transient (subfolder's id changes from 4 to 0!).
How can that be?
Here is my mapping file:
<?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>
<class name="test.Folder" table="FOLDERS">
<id name="id" type="long" access="field">
<column name="FOLDER_ID" />
<generator class="native" />
</id>
<set name="childFolders" table="FOLDERS" lazy="false" inverse="true" cascade="none">
<key column="PARENT_FOLDER_ID" not-null="false"></key>
<one-to-many class="test.Folder" />
</set>
<many-to-one name="parentFolder" column="PARENT_FOLDER_ID" />
<property name="name" column="FOLDER_NAME" />
<property name="rootFolder" column="IS_ROOT_FOLDER" type="boolean" not-null="true" />
<property name="path" column="FOLDER_PATH" />
<property name="type" column="FOLDER_TYPE" />
<property name="fullPath" column="FULL_PATH" unique="true" not-null="true" />
</class>
</hibernate-mapping>
Update: Here is the Java code I use to update the folder:
public class DatabaseController{
private SessionFactory sessionFactory = null;
public void updateFolder(Folder folder){
Session session = null;
Transaction transaction = null;
try {
session = getSession();
transaction = session.beginTransaction();
session.update(folder);
transaction.commit();
} catch (Exception e) {
rollback(transaction);
closeSession();
} finally {
closeSession();
}
}
/*
* Returns the Hibernate session
*/
private Session getSession() {
if (_session == null) {
_session = getSessionFactory().getCurrentSession();
}
if (_session.isOpen() == false) {
_session = getSessionFactory().openSession();
}
return _session;
}
/**
* Returns the session factory
*
* #return The session factory
*/
public SessionFactory getSessionFactory() {
return sessionFactory;
}
}
That's correct
you have cascade="none" in your set definitions.
http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html_single/#objectstate-transitive
You need to set cascade="save-update".
I found a solution to the problem. Changing the following lines in my hibernate mapping file fixed the issue:
<set name="childFolders" table="FOLDERS" inverse="true" cascade="none">
<key column="PARENT_FOLDER_ID"></key>
<one-to-many class="test.Folder" />
</set>
I think, removing not-null="false" for the key did the trick here.
your code :
UPDATE 2
<set name="childFolders" table="FOLDERS" lazy="false" inverse="true" cascade="none">
<key column="PARENT_FOLDER_ID" not-null="false"></key>
<one-to-many class="test.Folder" /> // **this should point to child table not itself**
</set>
you have given wrong relation ship
my code :
<set cascade="all, delete-orphan" name="childTable" order-by="param" inverse="true">
<key>
<column name="p_id"/>
</key>
<one-to-many class="com.a.data.ChildTable"/>
</set>