-->
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.  [ 3 posts ] 
Author Message
 Post subject: Annotations, one-to-many creating duplicate child records
PostPosted: Mon Jan 18, 2010 7:05 am 
Newbie

Joined: Mon Jan 18, 2010 6:07 am
Posts: 4
Simplified DB tables:

I am experiencing problems when attempting to update a set on my entity class.

I have a one-to-many mapping defined which should be along that lines of the following:

Underlying DB structure:
Code:
-- Base Log record type.
CREATE TABLE LOG_RECORD(
   ID INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY,
        DATE_CREATED TIMESTAMP,
);

-- Specific text flavour of log record.
CREATE TABLE TXT_LOG(
   LOG_EVENT_ID INTEGER NOT NULL,

   DESCRIPTION VARCHAR(700) NOT NULL
);

-- A log entity is the parent object, it has a number of log records...
CREATE TABLE LOG (
   ID INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY,
   .....
);

-- The log record association is modelled by this table, marking a log record against a specific
-- Note, no FKs defined - assume hibernate will handle sets as expected.
CREATE TABLE LOG_ASSOCIATION(
        LOG_ID INTEGER NOT NULL,
        LOG_RECORD_ID INTEGER NOT NULL
);


Key Entity mapping:
Code:
@Entity
class Log{
   ...
   Set<LogRecord> logRecords = new HashSet<LogRecord>();
   ....

   @OneToMany(fetch = FetchType.LAZY, cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH, CascadeType.REMOVE})
   @JoinTable(   uniqueConstraints = @UniqueConstraint(columnNames = {"LOG_ID", "LOG_RECORD_ID" }),
         name = "LOG_ASSOCIATION", joinColumns = @JoinColumn(name = "LOG_ID"), inverseJoinColumns = @JoinColumn(name = "LOG_RECORD_ID"))
   public Set<LogRecordt> getLogRecords()
   {....
   }
}


The problem occurrs when I try to make a change to the set, this is done by retrieving the existing set (by querying for the correct Log, from its parent structure) I add the new LogRecord type to the Set and then save the updating Log object, but instead of simply adding the new object only, new instances of ALL the LogRecords are created which isnt what I want.

I would appreciate any suggestions on where the problem my be rooted, I don't believe my Database is at fault, although it is fairly simple in terms of defined constraints, but my annotation mapping is no different to many examples I have seen so I dont know where else to look.


Top
 Profile  
 
 Post subject: Re: Annotations, one-to-many creating duplicate child records
PostPosted: Tue Jan 19, 2010 9:46 am 
Newbie

Joined: Mon Jan 18, 2010 6:07 am
Posts: 4
Having undertaken a lot of investigation I still do not have an answer for this, however I do have a work around which hints as a number of possible reasons for the problem.

The work around was to perform ALL code relating to the update in the same method of my DAO instead of spread over successive calls to the DAO from my business service.

It's probably worth giving a bit more detail on my classe.

I have a Spring configured bean as a business service, this is injected with my DAO class which is actually a subclass of HibernateDAOSupport (so this might turn out to be a post for their forums) which is configured as follows:

Code:
   <bean id="bussinessService" class=com.spike.logger.business.LogService">
      <property name="MyDAO" ref="MyDAOTarget" />
   </bean>

   <!-- Configure Hibernate SessionFactory -->
   <bean id="sessionFactory"
      class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">

      <property name="hibernateProperties">
         <props>
            <prop key="hibernate.connection.driver_class">org.apache.derby.jdbc.EmbeddedDriver</prop>
            ....etc...
         </props>
      </property>
      <property name="annotatedClasses">
         <list>   .....</list>
      </property>
   </bean>

   <!--sessionFactory will get autowired-->
   <bean id="hibernateInterceptor" class="org.springframework.orm.hibernate3.HibernateInterceptor" autowire="byName" />
   
   <!--sessionFactory will get autowired-->
   <bean id="MyDAOTarget" class="com.spike.logger.dao.MyDAOImpl" autowire="byName" />
   
   <bean id="MyDAO" class="org.springframework.aop.framework.ProxyFactoryBean">
      <property name="proxyInterfaces" value="com.spike.logger.dao.MyDAO" />
      <property name="interceptorNames">
         <list>
            <value>hibernateInterceptor</value>
            <value>MyDAOTarget</value>
         </list>
      </property>
   </bean>


The work around is not ideal, as it means all buiness logic to manipulate existing persistant entities has to by put into the DAO, but it does suggest the issue is in the area of Session/Transaction handling and/or the session not being flushed as expected as opposed to my initial concern that the problem was with my Annotations.

I have not found a good resource or similar example of my use of the Spring base class to move forward with, so perhaps I should move towards the more direct use of Hibernate in my application.

Any comments welcome - am I the only one having this problem?


Top
 Profile  
 
 Post subject: Re: Annotations, one-to-many creating duplicate child records
PostPosted: Tue Jan 19, 2010 12:29 pm 
Newbie

Joined: Mon Jan 18, 2010 6:07 am
Posts: 4
A solution, well not quite, but getting closer!

Another Exception I was getting when making various changes was:
Code:
Cause: 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


I have managed to avoid the work around I found (above) but I still don't understand the root problem, however by changing the queries that retrieve the existing Entity from the database, and the query to save the changes I can avoid getting the duplication.

Making the following changes

Old code:
Code:

public void saveLog(Log log)   {
   getHibernateTemplate().merge(response);
}

// @NamedQuery(name="log.getLog", query="from Log l where l.id = :log_id")
//
public Log getLog(Integer log_id)   {
   org.hibernate.Query q = getSession().getNamedQuery("log.getLog");
   q.setInteger("log_id", log_id);
   q.setString("group_name", group);
   return (Log) q.uniqueResult();
}


New code:
Code:
public void saveLog(Log log)   {
   getHibernateTemplate().saveOrUpdate(response);
}

public Log getLog(String group, Integer log_id)   {
   String query = "from Log l where l.id = ? ";

   Object[] parameters = { log_id };

   List<Log> logs = (List<Log>) getHibernateTemplate().find(query, parameters);
   Log log = logs.iterator().next();
   return log;
}


by making the above changes I can manipulate my Domain class Instance from my business tier, and the save the changes without getting the join table record duplication I was getting, however I am unable to use the merge operation (perhaps I am/was misusing this). I would like to understnad why I had the problem I did, was I actually using 2 sessions at the same time, which were not synchronized in any wat resulting in both performing acceptable actions. It appears to me that the changed way of querying is the crux to my fix, but it doesnt seem a whole one if i (or any other) developer could easily call merge() and cause problems again.

What was the root problem?
Is there a better way to address my problem?


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 3 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.