-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 2 posts ] 
Author Message
 Post subject: problem with unidirectional one to many
PostPosted: Fri Apr 03, 2009 4:03 am 
Newbie

Joined: Mon Mar 23, 2009 5:56 am
Posts: 6
Hi !

I have a problem with unidirectional one to many association.

I tried some samples from the net. There are two classes

Group and Story

each group can have one ore more stories.

The List of Story ist annotated as OneToMany with mappedBy=parent_id (column in the stories table).

Unfortunately the parent_id is not updated with the group id when a Group object is stored, and an exception occurres, because of a constraint violation (parent_id 0 does not exist as an id in groups table)

I do not know whether this is normal behavior or not. Hibernate Annotations documentation tells you, that in the dependend class (the Story class) nothing has to be annotated.


The Group class
Code:

package test.onetomany.copy;

import java.util.*;

import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name = "groups")
public class Group {
    private int id;
    private String name;

    private List stories;

    public Group() {
    }

    public Group(String name) {
   this.name = name;
    }

    public void setId(int i) {
   System.out.println("g.setID=" + i);
   id = i;
    }

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    public int getId() {
   System.out.println("g.getID=" + id);
   return id;
    }

    public void setName(String n) {
   name = n;
    }

    public String getName() {
   return name;
    }

    public void setStories(List l) {
   stories = l;
    }

    @OneToMany(fetch = FetchType.EAGER, mappedBy = "parent_id", targetEntity = Story.class, cascade = { javax.persistence.CascadeType.ALL })
    public List getStories() {

   return stories;
    }
}




The Story class
Code:


package test.onetomany.copy;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "stories")
public class Story {
    private int id;
    private String info;

    private int parent_id;

    public int getParent_id() {
   return parent_id;
    }

    public void setParent_id(int parent_id) {
   System.out.println("s.setparent_id=" + parent_id);
   this.parent_id = parent_id;
    }

    public Story() {

    }

    public Story(String info) {
   this.info = info;
    }

    public void setId(int i) {
   System.out.println("s.setID=" + i);
   id = i;
    }

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    public int getId() {
   System.out.println("s.getID=" + id);
   return id;
    }

    public void setInfo(String n) {
   info = n;
    }

    public String getInfo() {
   return info;
    }
}



The Test class

Code:
package test.onetomany.copy;

import java.util.ArrayList;
import java.util.List;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;

public class Test {

    /**
     * @param args
     */

    public static void main(String[] args) throws Exception {

   Transaction transaction = null;
   try {
       Session session = HibernateUtil.getCurrentSession();

       transaction = session.beginTransaction();
       Group sp = new Group("Group Name");

       session.saveOrUpdate(sp);

       transaction.commit();

       session = HibernateUtil.getCurrentSession();
       transaction = session.beginTransaction();

       ArrayList list = new ArrayList();
       list.add(new Story("Story Name 1"));
       list.add(new Story("Story Name 2"));
       list.add(new Story("Story Name 3"));
       list.add(new Story("Story Name 4"));
       sp.setStories(list);

       session.saveOrUpdate(sp);
       transaction.commit();

       // reload
       session = HibernateUtil.getCurrentSession();
       transaction = session.beginTransaction();

       Query q = session.createQuery("from Group");
       List<Group> geometries = (List<Group>) q.list();
       for (Group g : geometries) {
      List<Story> stories = g.getStories();

      for (Story s : stories) {
          System.out.println("story info=" + s.getInfo());
      }
       }

       transaction.commit();

   } catch (Exception e) {
       if (transaction != null) {
      transaction.rollback();
      throw e;
       }
   } finally {
       // session.close();
   }

    }
}


Hibernate config looks like this,
Database is automatically generated

Code:
<?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="SessionFactory">
  <property name="hibernate.connection.driver_class">org.postgresql.Driver</property>

  <property name="hibernate.connection.url">jdbc:postgresql://localhost:5433/postgis</property>
  <property name="hibernate.connection.password">postgres</property>
  <property name="hibernate.connection.username">postgres</property>
  <!-- <property name="hibernate.connection.catalog">postgis</property>  -->
  <!-- <property name="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</property> -->
  <!-- <property name="hibernate.dialect">org.hibernatespatial.postgis.PostgisDialect</property> -->
 
 
  <!-- Enable Hibernate's automatic session context management -->
        <property name="current_session_context_class">thread</property>
 
  <!--<property name="hibernate.current_session_context_class">thread</property> -->
 
  <!-- Disable the second-level cache  -->
  <property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
  <property name="hibernate.show_sql">true</property>
  <property name="hibernate.format_sql">true</property>
  <!-- Drop and re-create the database schema on startup -->
  <property name="hibernate.hbm2ddl.auto">create</property>
 
  <mapping class="test.onetomany.copy.Group"/>
  <mapping class="test.onetomany.copy.Story"/>



</session-factory>
</hibernate-configuration>





finally the code of the HibernatUtil class

Code:

package test.onetomany.copy;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.cfg.Configuration;


public class HibernateUtil {

  private static SessionFactory sessionFactory;

  static {
    createSessionFactory();
  }
 
  private static void createSessionFactory() {
    try {
      // Create the SessionFactory
     
   String path = HibernateUtil.class.getPackage().getName().replace('.','/');
   System.out.println("path=" + path);
   sessionFactory = new AnnotationConfiguration().configure(path + "/hibernate.cfg.xml")
          .buildSessionFactory();     
    }
    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);
    }
  }
 
  // Creating a SessionFactory ist time consuming. This should
  // be done only once per database. SessionFactory is thread-safe.
  public static SessionFactory getSessionFactory() {
    if (sessionFactory == null)
      createSessionFactory();
   
    return sessionFactory;
  }

  // Version 2:
  // Session is a lightweight component, but it isn't thread-safe.
  // Therefore, a separate connection has to be opened for each connection.
  // Hibernate 3 offers a tailor-made solution for this task:
  // getCurrentSession() delivers a different session for each thread.
  // Don't forget to set the property
  //    hibernate.current_session_context_class = thread.
  // Moreover, the session is closed if the associated transaction is
  // committed or rollbacked.
  public static Session getCurrentSession() {
    return getSessionFactory().getCurrentSession();
  }

  public static void closeSessionFactory() {
    sessionFactory.close();
   
  }
 
  public static void saveOrUpdate(Object object) {
         Session s = HibernateUtil.getCurrentSession();
   Transaction tx = s.beginTransaction();
   s.saveOrUpdate(object);
   tx.commit();
  }
 
 
 
 
 
 
}




Hibernate version:
3, Annotations 3.2.1
hibernate spatial postgis 1.0 M2
Mapping documents:

Code between sessionFactory.openSession() and session.close():

Session session = HibernateUtil.getCurrentSession();

transaction = session.beginTransaction();
Group sp = new Group("Group Name");

session.saveOrUpdate(sp);

transaction.commit();

session = HibernateUtil.getCurrentSession();
transaction = session.beginTransaction();

ArrayList list = new ArrayList();
list.add(new Story("Story Name 1"));
list.add(new Story("Story Name 2"));
list.add(new Story("Story Name 3"));
list.add(new Story("Story Name 4"));
sp.setStories(list);

session.saveOrUpdate(sp);
transaction.commit();

Full stack trace of any exception that occurs:

Exception in thread "main" org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:94)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:275)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:266)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:167)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:50)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1027)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:365)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:137)
at test.onetomany.copy.Test.main(Test.java:44)
Caused by: java.sql.BatchUpdateException: Batch-Eintrag 0 insert into stories (info, parent_id, id) values (Story Name 1, 0, 2) wurde abgebrochen. Rufen Sie 'getNextException' auf, um die Ursache zu erfahren.
at org.postgresql.jdbc2.AbstractJdbc2Statement$BatchResultHandler.handleError(AbstractJdbc2Statement.java:2534)
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1317)
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:350)
at org.postgresql.jdbc2.AbstractJdbc2Statement.executeBatch(AbstractJdbc2Statement.java:2596)
at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:70)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:268)
... 8 more

Name and version of the database you are using:

Postgres 8.3

The generated SQL (show_sql=true):

s.getID=0
g.getID=0
g.getID=0
Hibernate:
select
nextval ('hibernate_sequence')
g.setID=1
g.getID=1
g.getID=1
Hibernate:
insert
into
groups
(name, id)
values
(?, ?)
g.getID=1
g.getID=1
s.getID=0
Hibernate:
select
nextval ('hibernate_sequence')
s.setID=2
s.getID=0
Hibernate:
select
nextval ('hibernate_sequence')
s.setID=3
s.getID=0
Hibernate:
select
nextval ('hibernate_sequence')
s.setID=4
s.getID=0
Hibernate:
select
nextval ('hibernate_sequence')
s.setID=5
g.getID=1
s.getID=2
s.getID=3
s.getID=4
s.getID=5
s.getID=5
s.getID=2
g.getID=1
s.getID=2
s.getID=3
s.getID=4
s.getID=5
s.getID=4
s.getID=3
Hibernate:
insert
into
stories
(info, parent_id, id)
values
(?, ?, ?)
Hibernate:
insert
into
stories
(info, parent_id, id)
values
(?, ?, ?)
Hibernate:
insert
into
stories
(info, parent_id, id)
values
(?, ?, ?)
Hibernate:
insert
into
stories
(info, parent_id, id)
values
(?, ?, ?)





Note:

I do not want to introduce a backward reference for bidirectional association. I know that it works with a backward reference, but consider story is an object which can be referenced and stored by several different objects.

I ve tried to insert a ManyToOne annotation at parent_id in Story, but then hibernate wants an entity type.


currently my workaround is to update the id myself in the
getStories method of Group:

Code:
if (stories != null) {
     for (Story s: (List<Story>)stories ) {
         s.setParent_id(getId());
     }
      }


Maybe somebody has an idea, why this does not work. I think solution will be quite simple, but i dont see it now.

regards

Michael


Top
 Profile  
 
 Post subject: problem with unidirectional one to many
PostPosted: Sat Apr 11, 2009 3:54 pm 
Newbie

Joined: Sat Apr 11, 2009 3:37 pm
Posts: 1
Location: Warsaw, Poland
I had very similar problem and in my case resolution was adding equals nad hashCode for entity on "many" side of relationship. I hope that it will help in your case.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 2 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.