I have a database that contain 2 tables that connect by one-to-many relationship. I enteract with that database through Hibernate.
This is hbm.xml files:
question.hbm.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="app">
<class name="Question" table="Question">
<id name="id" column="id" type="java.lang.Long">
<generator class="native"/>
</id>
<property name="text" column="text" type="java.lang.String" not-null="true"/>
<set name="answers" cascade="all">
<key column="question_id"/>
<one-to-many class="Answer"/>
</set>
</class>
</hibernate-mapping>
answer.hbm.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="app">
<class name="Answer" table="Answer">
<id name="id" column="id" type="java.lang.Long">
<generator class="native"/>
</id>
<property name="text" column="text" type="java.lang.String" not-null="true"/>
<many-to-one name="question" column="question_id" class="Question" not-null="true"/>
</class>
</hibernate-mapping>
And this is java files:
Question.java
package app;
import java.util.Set;
public class Question {
Long id = null;
String text = "";
Set<Answer> answers = null;
// getters and setters
}
Answer.java
package app;
public class Answer {
Long id = null;
String text = "";
boolean isTrue = false;
Question question = null;
// getters and setters
}
So Question can have Answers and Question instance has a collection fo Answers instances.
This is main method:
package application;
import app.Answer;
import app.Question;
import java.util.HashSet;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class Application {
public static void main(String[] args) {
SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
Session session = sessionFactory.openSession();
Question question = new Question();
question.setText("question1");
Answer answer1 = new Answer();
answer1.setText("answer1");
answer1.setQuestion(question);
Answer answer2 = new Answer();
answer2.setText("answer2");
answer2.setQuestion(question);
HashSet<Answer> answers = new HashSet<>();
answers.add(answer1);
answers.add(answer2);
question.setAnswers(answers);
session.beginTransaction();
try {
session.saveOrUpdate(question);
session.getTransaction().commit();
} catch (Exception ex) {
ex.printStackTrace();
session.getTransaction().rollback();
}
answers.remove(answer2);
session.beginTransaction();
try {
session.saveOrUpdate(question);
session.getTransaction().commit();
} catch (Exception ex) {
ex.printStackTrace();
session.getTransaction().rollback();
}
}
}
So I create Question instance, add two Answers instances to collection in Question instance and save Question instance in database. All work correctly - Question and Answers is writen to database.
Then I remome one of Answers instance from collection in Question instance and try to save Question instance in database again. I catch an Exception. It's in my native language, but it is SQLServerException and it say something like this: "Can't insert NULL in column "question_id" in table "Test.dbo.Answer", in this column NULL is forbidden. Error in UPDATE"
What's wrong? How can I delete one of Answer from Question?
In a bidirectional relationship one side has to be the inverse one. In a one-to-many association that is usually the one- side:
<set name="answers" cascade="all" inverse="true">
<key column="question_id"/>
<one-to-many class="Answer"/>
</set>
This way only many- side will take care of the relationship.
In your case, both sides do it, and since the foreign key is in the Answer class/table, Hibernate updates it to null when flushing the association changes in the Question class.
saveOrUpdate() is only for createing a new record if doesnt exist or updating if it does exist.Please find the pseudocode below.
Question question = new Question();
question.setText("question1");
Answer deleteAns=new Answer();
deleteAns.setText("answer2");
question.setAnswer(deleteAns);
session.delete(question)
Related
I am trying to run following code in eclipse
package com.trial;
import java.sql.*;
import java.io.*;
import org.hibernate.*;
import org.hibernate.cfg.*;
public class AddStudent {
private static SessionFactory sessionFactory;
public static void main(String args[]) throws Exception {
DataInputStream d = new DataInputStream(System.in);
System.out.println("ENTER YOUR NAME");
String name = d.readLine();
System.out.println("ENTER YOUR DEGREE");
String degree = d.readLine();
System.out.println("ENTER YOUR PHONE");
int phone = Integer.parseInt(d.readLine());
System.out.println("Name: " + name);
System.out.println("Degree: " + degree);
System.out.println("Phone: " + phone);
if ((name.equals("") || degree.equals(""))) {
System.out.println("Information Required");
}
else {
try {
sessionFactory = new Configuration().configure("com//xml//hibernate.cfg.xml").buildSessionFactory();
}
catch (Exception e) {
System.out.println(e.getMessage());
}
Session s = sessionFactory.openSession();
Student stu = new Student();
stu.setName(name);
stu.setDegree(degree);
stu.setPhone(phone);
s.save(stu);
System.out.println("Added to Database");
if (s != null)
s.close();
}
}
}
But getting Runtime exception during creating session factory object that Unable to read XML.
I am using following xml files
hibernate.cfg.xml
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory name="studentFactory">
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost:3306/ganeshdb</property>
<property name="connection.username">****</property>
<property name="connection.password">****</property>
<property name="connection.pool_size">10</property>
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="show_sql">true</property>
<property name="hbm2ddl.auto">create</property>
<mapping resource="com//xml//student.hbm.xml" />
</session-factory>
</hibernate-configuration>
Mapping File
<?xml version="1.0"? encoding='UTF-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/hibernate-configuration-3.0.dtd">
<hibernate-mapping>
<class name="com.trial.Student" table="studentdemo">
<id name="id" type="int" column="ID">
<generator class="increment" />
</id>
<property name="name" column="name" />
<property name="degree" column="degree" />
<property name="phone" column="phone" />
</class>
</hibernate-mapping>
plz help.
<mapping resource="student.hbm.xml" />
and make sure it is in same direcotry as hibernate.cfg.xml
im new in hibernate and try to write data in DB.
my code
hibernate.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory name="postsess">
<property name="hibernate.connection.driver_class">org.postgresql.Driver</property>
<property name="hibernate.connection.password">123456</property>
<property name="hibernate.connection.url">jdbc:postgresql://localhost:5432/postgis</property>
<property name="hibernate.connection.username">postgres</property>
<property name="hibernate.default_schema">public</property>
<property name="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</property>
<!-- Mapping files -->
<mapping resource="net.sf.hibernate.examples.quickstart.Cat.hbm.xml"/>
</session-factory>
</hibernate-configuration>
HibernateUtin
package net.sf.hibernate.examples.quickstart;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import net.sf.hibernate.*;
//import net.sf.hibernate.examples.quickstart.hibernate.cfg.xml;
public class HibernateUtil {
private static final SessionFactory sessionFactory;
static {
try {
// Create the SessionFactory
sessionFactory = new Configuration().configure().buildSessionFactory();
} catch (HibernateException ex) {
throw new RuntimeException("Configuration problem: " + ex.getMessage(), ex);
}
}
public static final ThreadLocal session = new ThreadLocal();
public static Session currentSession() throws HibernateException {
Session s = (Session) session.get();
// Open a new Session, if this Thread has none yet
if (s == null) {
s = sessionFactory.openSession();
session.set(s);
}
return s;
}
public static void closeSession() throws HibernateException {
Session s = (Session) session.get();
session.set(null);
if (s != null)
s.close();
}
}
And get an exeption
log4j:WARN No appenders could be found for logger (org.hibernate.cfg.Environment).
log4j:WARN Please initialize the log4j system properly.
Exception in thread "main" java.lang.ExceptionInInitializerError
at net.sf.hibernate.examples.quickstart.hib_main.main(hib_main.java:14)
Caused by: java.lang.RuntimeException: Configuration problem: Resource: net.sf.hibernate.examples.quickstart.Cat.hbm.xml not found
at net.sf.hibernate.examples.quickstart.HibernateUtil.<clinit> (HibernateUtil.java:20)
... 1 more
Caused by: org.hibernate.MappingException: Resource: net.sf.hibernate.examples.quickstart.Cat.hbm.xml not found
at org.hibernate.cfg.Configuration.addResource(Configuration.java:444)
at org.hibernate.cfg.Configuration.parseMappingElement(Configuration.java:1313)
at org.hibernate.cfg.Configuration.parseSessionFactory(Configuration.java:1285)
at org.hibernate.cfg.Configuration.doConfigure(Configuration.java:1267)
at org.hibernate.cfg.Configuration.doConfigure(Configuration.java:1234)
at org.hibernate.cfg.Configuration.configure(Configuration.java:1162)
at org.hibernate.cfg.Configuration.configure(Configuration.java:1148)
at net.sf.hibernate.examples.quickstart.HibernateUtil.<clinit>(HibernateUtil.java:18)
... 1 more
What im doing wrong. It a second try to execute simple example and again same exeption.
UPDATE
after changing path to mapping file in hibernate.cfg.xml i get another exeption in line session.save(princess);
log4j:WARN No appenders could be found for logger (org.hibernate.cfg.Environment).
log4j:WARN Please initialize the log4j system properly.
Exception in thread "main" org.hibernate.id.IdentifierGenerationException: ids for this class must be manually assigned before calling save(): net.sf.hibernate.examples.quickstart.Cat
at org.hibernate.id.Assigned.generate(Assigned.java:32)
at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:85)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:184)
at org.hibernate.event.def.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(DefaultSaveEventListener.java:33)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:173)
at org.hibernate.event.def.DefaultSaveEventListener.performSaveOrUpdate(DefaultSaveEventListener.java:27)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:69)
at org.hibernate.impl.SessionImpl.save(SessionImpl.java:477)
at org.hibernate.impl.SessionImpl.save(SessionImpl.java:472)
at net.sf.hibernate.examples.quickstart.hib_main.main(hib_main.java:23)
Cat.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 15.08.2012 12:46:22 by Hibernate Tools 3.4.0.CR1 -->
<hibernate-mapping>
<class name="net.sf.hibernate.examples.quickstart.Cat" table="CAT">
<id name="id" type="java.lang.String">
<column name="cat_id" />
<generator class="assigned" />
</id>
<property name="name" type="java.lang.String">
<column name="name" />
</property>
<property name="sex" type="char">
<column name="sex" />
</property>
<property name="weight" type="float">
<column name="weight" />
</property>
</class>
</hibernate-mapping>
and main class.
package net.sf.hibernate.examples.quickstart;
import org.hibernate.Session;
import org.hibernate.Transaction;
public class hib_main
{
/**
* #param args
*/
public static void main(String[] args)
{
Session session = HibernateUtil.currentSession();
Transaction tx= session.beginTransaction();
Cat princess = new Cat();
princess.setName("Princess");
princess.setSex('F');
princess.setWeight(7.4f);
session.save(princess);
tx.commit();
HibernateUtil.closeSession();
}
}
Try to define your mapping resource in such way (using "/" instead of "."):
<mapping resource="net/sf/hibernate/examples/quickstart/Cat.hbm.xml"/>
I think this is a reason of your problem.
UPDATE:
About your second problem - I think that you forgot to define id generation strategy in the Cat.hbm.sql. For example:
<id name="id" column="CREDIT_CARD_ID" type="long">
<generator class="native"></generator>
</id>
UPDATE 2:
Hibernate uses it's own types. So in the declaration of id type yous should use not java.lang.String or java.lang.Integer, but simple string and integer. For example if you want to use string id you should define it as:
<id name="id" column="cat_id" type="integer">
<generator class="native"/>
</id>
Here you can find the list of hibernate's mapping types.
You have two hibernate.cfg.xml.So remove one hibernate.cfg.xml.Try to test with below structure.And resource mapping is not trouble to you.You can declare only like this
<mapping resource="Cat.hbm.xml"/>
You should use / infront of path:
<mapping resource="/net/sf/hibernate/examples/quickstart/Cat.hbm.xml"/>
I would rename Cat.hbm.xml as Cat_hbm_xml as each '.' referring to a sub package.
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>
I'm using persistence in a project for school, and I have a problem when I'm try to deleting and updating object, all others queries works.
The exception is :
Illegal attempt to associate a collection with two open sessions
I close every session I have opened.
HibernateUtils code
public class Hibernate
{
protected static final SessionFactory sessionFactory;
private Session session;
static
{
try
{
// Create the SessionFactory from hibernate.cfg.xml
sessionFactory = new Configuration().configure().buildSessionFactory();
session
}
catch (Throwable ex)
{
// Make sure you log the exception, as it might be swallowed
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public void create(Object obj)
{
this.session = sessionFactory.openSession();
session.getTransaction().begin();
session.save(obj);
session.getTransaction().commit();
session.close();
}
public void refresh(Object obj)
{
this.session = sessionFactory.openSession();
session.getTransaction().begin();
session.refresh(obj);
session.getTransaction().commit();
session.close();
}
public void update(Object obj)
{
this.session = sessionFactory.openSession();
session.getTransaction().begin();
session.saveOrUpdate(obj);
session.getTransaction().commit();
session.close();
}
public void delete(Object obj)
{
this.session = sessionFactory.openSession();
session.getTransaction().begin();
session.delete(obj);
session.flush();
session.getTransaction().commit();
session.close();
}
protected String protectString(String toProtect)
{
return (toProtect.replace("'", "''"));
}
}
DAOPerson :
public class DAOPerson extends Hibernate
{
public void remove(Person p)
{
if (p instanceof Student)
{
Student s = (Student)p;
Set<persistenceClass.Class> set = s.getClasses();
Iterator<persistenceClass.Class> it = set.iterator();
while (it.hasNext())
{
persistenceClass.Class r = it.next();
r.getStudents().remove(s);
}
p.getBirthCountry();
p.getCountry();
this.delete(p);
}
else
this.delete(p);
}
For information my mapping file of students is :
<class name="persistenceClass.Person" table="T_PERSON">
<id name="Id" column="PERSON_ID">
<generator class="native" />
</id>
<property name="FirstName" column="PERSON_FIRST_NAME" not-null="true" />
<property name="LastName" column="PERSON_LAST_NAME" not-null="true" />
<property name="Type" column="PERSON_TYPE" not-null="true" />
<property name="BirthDate" column="PERSON_BIRTH_DATE" />
<property name="BirthCity" column="PERSON_BIRTH_CITY" />
<property name="PhoneNumber" column="PERSON_PHONE_NUMBER" />
<property name="MobileNumber" column="PERSON_MOBILE_NUMBER" />
<property name="Mail" column="PERSON_MAIL" />
<property name="Address" column="PERSON_ADDRESS_ADDRESS" />
<property name="ZipCode" column="PERSON_ADDRESS_ZIPCODE" />
<property name="City" column="PERSON_ADDRESS_CITY" />
<property name="Image" column="PERSON_IMAGE" type="image" />
<many-to-one name="Country" column="PERSON_ADDRESS_COUNTRY" class="persistenceClass.Country" />
<many-to-one name="BirthCountry" column="PERSON_BIRTH_COUNTRY" class="persistenceClass.Country" />
<many-to-one name="Civility" column="PERSON_CIVILITY" class="persistenceClass.Civility" />
<many-to-one name="Sex" column="PERSON_SEX" class="persistenceClass.Sex" />
<joined-subclass name="persistenceClass.Student" table="T_STUDENT">
<key column="PERSON_ID" />
<set name="Classes" table="T_CLASS_STUDENT" inverse="true" >
<key column="PERSON_ID" />
<many-to-many class="persistenceClass.Class" column="CLASS_ID" />
</set>
</joined-subclass>
<joined-subclass name="persistenceClass.Teacher" table="T_TEACHER">
<key column="PERSON_ID" />
</joined-subclass>
</class>
And the main mapping file :
<hibernate-configuration>
<session-factory>
<!-- Database connection settings -->
<property name="connection.driver_class">org.gjt.mm.mysql.Driver</property>
<property name="connection.url">jdbc:mysql://localhost/projet</property>
<property name="connection.username">root</property>
<property name="connection.password"></property>
<!-- JDBC connection pool (use the built-in) -->
<property name="connection.pool_size">10</property>
<!-- SQL dialect -->
<property name="dialect">org.hibernate.dialect.MySQLInnoDBDialect</property>
<!-- Drop and re-create the database schema on start-up, also try with “update” to keep the previous values -->
<property name="hbm2ddl.auto">update</property>
<!-- Echo all executed SQL to stdout -->
<property name="show_sql">true</property>
<mapping resource="persistenceConfigurations/Person.hbm.xml"/>
<mapping resource="persistenceConfigurations/Country.hbm.xml"/>
<mapping resource="persistenceConfigurations/Civility.hbm.xml"/>
<mapping resource="persistenceConfigurations/Sex.hbm.xml"/>
<mapping resource="persistenceConfigurations/Formation.hbm.xml"/>
<mapping resource="persistenceConfigurations/Year.hbm.xml"/>
<mapping resource="persistenceConfigurations/Class.hbm.xml"/>
<mapping resource="persistenceConfigurations/Subject.hbm.xml"/>
<mapping resource="persistenceConfigurations/Room.hbm.xml"/>
<mapping resource="persistenceConfigurations/Lesson.hbm.xml"/>
</session-factory>
</hibernate-configuration>
I try a lot of configuration but I've everytime the same exception, if somebody have an idea, I want it !
Thanks !
Sorry for my bad english
The session is not meant to be used for one call and closed. Using the utility class as a base for your POJO is not a good idea.
The entity that is being deleted should use the same session that it was retrieved from or at least be refreshed on the new session before being removed.
Also the iterating through the removal of the dependent 'Class' entities should be replaced with a cascade REMOVE.
Try using getCurrentSession() instead of openSession() and remove the session.close(); statement.
for starters I think it's a good idea to refactor the name you are giving to the concept of Class (path persistenceClass.Class). In java world 'Class' is actually a class. That may cause some havoc in the future.
One problem I realized on your DAOPerson code is that it looks strange. I understood you're trying to remove the current student from its classes, correct? if so, try the following patch on your code:
public void remove(Person p)
{
if (p instanceof Student)
{
Student s = (Student)p;
Set<persistenceClass.Class> set = s.getClasses();
Iterator<persistenceClass.Class> it = set.iterator();
while (it.hasNext())
{
persistenceClass.Class r = it.next();
r.getStudents().remove(s);
//now effectively removing the student from the class.
//this will update the class and its persistent set of students.
this.update(r);
}
}
this.delete(p); //now remove the student (or whatever comes as argument)
}
Now another possible problem: your HibernateUtils is not treating transactions properly. You should refactor your code to look like the following:
//Example
public void create(Object obj){
this.session = sessionFactory.openSession();
try{
session.getTransaction().begin();
session.save(obj);
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
} finally {
session.flush();
session.close();
//keep in mind that too many flushes might cause db memory shortage.
//So if you are treating list of objects and so on iterate and save them flushing
//only when your batch size is reached.
}
}
Hope that helps!
I notice that you haven't defined lazy=false in you person mapping file, so I assume you are using lazy loading attribute for the property Set. You say you have closed all sessions before a call to remove, then how come you are able to access the property s.getClasses() in the remove() method of DAOPerson without throwing a lazyInitializationException. If you forgot to mention lazy=false in the mapping file, then my observations do not count. If that is not the case, then you surely do have a session open somewhere to which the Person p (passed to the DAOs remove method) is associated. At the end of this method, when you try to re-attach the object with another session you get an exception.
The problem was due to a misuse of cascade update in one of the mappings. Here is a sample field mapping:
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = “user_id”)
public User getAuthor() {
return author;
}
Removing the cascade = CascadeType.ALL fixed the problem. Conclusion: carefully use cascade updates as it may get you into trouble. Use it when business logic requires it. In the example below there was no need for it, so removing it was both business and programatically a good decision.
Source: http://blog.tremend.ro/2007/03/05/illegal-attempt-to-associate-a-collection-with-two-open-sessions/