-->
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.  [ 4 posts ] 
Author Message
 Post subject: Indexing entity with composite identifier
PostPosted: Mon Sep 05, 2011 7:24 am 
Expert
Expert

Joined: Tue Dec 28, 2004 7:02 am
Posts: 573
Location: Toulouse, France
Hi all,

I'm an Hibernate Search newbie. I've already used hibernate search in the past for very dead-simple use cases, but now I'm having issues trying to index an entity whose primary key is a composite one, composed of both a String and a date.
I read the reference documentation and browsed the forum and didn't find that use case.

I'm about to write a custom FieldBridge for my @Embeddable class, but I'm not sure it's the right way to go. So I though I'd ask the question here.
Here's the code:
Code:
@Table(name = "R_ACTE")
@Indexed
@Entity
public class CActe {
   @Embeddable
   public static class CActeId implements Serializable {
      private String codeActe;
      
      private Date dateModification;

      @Field
      @Temporal(TemporalType.DATE)
      @Column(name="DT_MODIF")
      public Date getDateModification() {
         return dateModification;
      }

      public void setDateModification(Date dateEffet) {
         this.dateModification = dateEffet;
      }

      @Field
      @Column(name="COD_ACTE")
      public String getCodeActe() {
         return codeActe;
      }

      public void setCodeActe(String codeActe) {
         this.codeActe = codeActe;
      }
   }

   private CActeId id;

   @Id
   public CActeId getId() {
      return id;
   }

   public void setId(CActeId id) {
      this.id = id;
   }

   private String nomCourt;

   @Field
   @Column(name = "NOM_COURT")
   public String getNomCourt() {
      return nomCourt;
   }

   public void setNomCourt(String nomCourt) {
      this.nomCourt = nomCourt;
   }

   private String nomLong;

   @Field
   @Column(name = "NOM_LONG")
   public String getNomLong() {
      return nomLong;
   }

   public void setNomLong(String nomLong) {
      this.nomLong = nomLong;
   }
}


When I start indexing, I receive the following exception:
Code:
org.hibernate.HibernateException: could not init listeners
   at org.hibernate.event.EventListeners.initializeListeners(EventListeners.java:205) [hibernate-core-3.5.4-Final.jar:3.5.4-Final]
   at org.hibernate.cfg.Configuration.getInitializedEventListeners(Configuration.java:1396) [hibernate-core-3.5.4-Final.jar:3.5.0-Final]
   at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1385) [hibernate-core-3.5.4-Final.jar:3.5.0-Final]
   at org.hibernate.cfg.AnnotationConfiguration.buildSessionFactory(AnnotationConfiguration.java:954) [hibernate-annotations-3.5.0-Final.jar:3.5.0-Final]
   at fr.pgih.poc.AppTest.setUp(AppTest.java:126) [test-classes/:na]
   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) [na:1.6.0_26]
   at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) [na:1.6.0_26]
   at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) [na:1.6.0_26]
   at java.lang.reflect.Method.invoke(Method.java:597) [na:1.6.0_26]
   at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44) [junit-4.9.jar:na]
   at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15) [junit-4.9.jar:na]
   at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41) [junit-4.9.jar:na]
   at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:27) [junit-4.9.jar:na]
   at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31) [junit-4.9.jar:na]
   at org.junit.runners.ParentRunner.run(ParentRunner.java:292) [junit-4.9.jar:na]
   at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:46) [.cp/:na]
   at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) [.cp/:na]
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467) [.cp/:na]
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683) [.cp/:na]
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390) [.cp/:na]
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197) [.cp/:na]
Caused by: org.hibernate.search.SearchException: Unable to guess FieldBridge for id
   at org.hibernate.search.bridge.BridgeFactory.guessType(BridgeFactory.java:229) [hibernate-search-3.2.1.Final.jar:3.2.1.Final]
   at org.hibernate.search.engine.DocumentBuilderIndexedEntity.checkDocumentId(DocumentBuilderIndexedEntity.java:191) [hibernate-search-3.2.1.Final.jar:3.2.1.Final]
   at org.hibernate.search.engine.DocumentBuilderContainedEntity.initializeMemberLevelAnnotations(DocumentBuilderContainedEntity.java:226) [hibernate-search-3.2.1.Final.jar:3.2.1.Final]
   at org.hibernate.search.engine.DocumentBuilderContainedEntity.initializeClass(DocumentBuilderContainedEntity.java:166) [hibernate-search-3.2.1.Final.jar:3.2.1.Final]
   at org.hibernate.search.engine.DocumentBuilderContainedEntity.init(DocumentBuilderContainedEntity.java:133) [hibernate-search-3.2.1.Final.jar:3.2.1.Final]
   at org.hibernate.search.engine.DocumentBuilderIndexedEntity.init(DocumentBuilderIndexedEntity.java:156) [hibernate-search-3.2.1.Final.jar:3.2.1.Final]
   at org.hibernate.search.engine.DocumentBuilderContainedEntity.<init>(DocumentBuilderContainedEntity.java:119) [hibernate-search-3.2.1.Final.jar:3.2.1.Final]
   at org.hibernate.search.engine.DocumentBuilderIndexedEntity.<init>(DocumentBuilderIndexedEntity.java:148) [hibernate-search-3.2.1.Final.jar:3.2.1.Final]
   at org.hibernate.search.impl.SearchFactoryImpl.initDocumentBuilders(SearchFactoryImpl.java:519) [hibernate-search-3.2.1.Final.jar:3.2.1.Final]
   at org.hibernate.search.impl.SearchFactoryImpl.<init>(SearchFactoryImpl.java:171) [hibernate-search-3.2.1.Final.jar:3.2.1.Final]
   at org.hibernate.search.event.FullTextIndexEventListener.initialize(FullTextIndexEventListener.java:126) [hibernate-search-3.2.1.Final.jar:3.2.1.Final]
   at org.hibernate.event.EventListeners$1.processListener(EventListeners.java:198) [hibernate-core-3.5.4-Final.jar:3.5.4-Final]
   at org.hibernate.event.EventListeners.processListeners(EventListeners.java:181) [hibernate-core-3.5.4-Final.jar:3.5.4-Final]
   at org.hibernate.event.EventListeners.initializeListeners(EventListeners.java:194) [hibernate-core-3.5.4-Final.jar:3.5.4-Final]
   ... 20 common frames omitted


So, is writing a custom FieldBridge for my CActeId class the way to go?

Thanks for any hint.
-- Baptiste
PS : Note I'm not using the last version of Hibernate Search on purpose since it'll have to work inside JBoss EAP 5.x.

_________________
Baptiste
PS : please don't forget to give credits below if you found this answer useful :)


Top
 Profile  
 
 Post subject: Re: Indexing entity with composite identifier
PostPosted: Mon Sep 05, 2011 8:32 am 
Hibernate Team
Hibernate Team

Joined: Fri Oct 05, 2007 4:47 pm
Posts: 2536
Location: Third rock from the Sun
Quote:
So, is writing a custom FieldBridge for my CActeId class the way to go?

Yes, but it has to be a org.hibernate.search.bridge.TwoWayFieldBridge, as an additional requirement for identifiers only we need to be able to to a two-way conversion.

_________________
Sanne
http://in.relation.to/


Top
 Profile  
 
 Post subject: Re: Indexing entity with composite identifier
PostPosted: Mon Sep 05, 2011 9:10 am 
Expert
Expert

Joined: Tue Dec 28, 2004 7:02 am
Posts: 573
Location: Toulouse, France
Hi, Thanks for your answer.

Ok, so I just wrote my bridge. I just wanted a very simple indexer, so I used a delegation for the date part. Here it is:
Code:
public class CActeIdFieldBridge implements TwoWayStringBridge {
   private static final char SEPARATOR = '#';
   DateBridge dateBridge = new DateBridge();

   public Object stringToObject(String stringValue) {
      int separatorIndex = stringValue.indexOf(SEPARATOR);
      String codeActe = stringValue.substring(0, separatorIndex);
      Date theDate = (Date) dateBridge.stringToObject(stringValue.substring(separatorIndex, stringValue.length()));

      CActeId id = new CActeId();
      id.setCodeActe(codeActe);
      id.setDateModification(theDate);
      return id;
   }

   public String objectToString(Object object) {
      CActeId id = (CActeId) object;
      return id.getCodeActe() + SEPARATOR + dateBridge.objectToString(id.getDateModification());
   }
}


What do you think?

Does it seem correct from a hibernate search point of view?
At the moment, I'm still having a hard-time seeing all the implications behind.

Also, for example, I don't totally understand why Hibernate Search does not provide default/automatic bridges for this use case. I mean technically it would quite easy to do a generic bridge for composite classes like this, using reflections it would then compose bridges for each basic types already covered, isn't it?. As I know the level of the team is very high, I suppose it was not made on purpose. Maybe for performance implications?

Thanks a lot again.

_________________
Baptiste
PS : please don't forget to give credits below if you found this answer useful :)


Top
 Profile  
 
 Post subject: Re: Indexing entity with composite identifier
PostPosted: Mon Sep 05, 2011 9:32 am 
Hibernate Team
Hibernate Team

Joined: Fri Oct 05, 2007 4:47 pm
Posts: 2536
Location: Third rock from the Sun
Quote:
What do you think?

Does it seem correct from a hibernate search point of view?
At the moment, I'm still having a hard-time seeing all the implications behind.

Yes that seems the way to go, assuming you're aware of the limitations I mention in the next paragraph (uniqueness of '#' and Date precision requirements).

Quote:
Also, for example, I don't totally understand why Hibernate Search does not provide default/automatic bridges for this use case. I mean technically it would quite easy to do a generic bridge for composite classes like this, using reflections it would then compose bridges for each basic types already covered, isn't it?.

It's not always straight-forward to map data to String and back. For example your solution will fail if your codeActe happens to contain a "#"; we provide out-of-the-box conversions for many custom types, but combining them has some perils for which the user should best be aware of, and do some explicit choices, for example around required precision in Date conversions.

Quote:
As I know the level of the team is very high, I suppose it was not made on purpose.

Thank you :) but in this case I think we didn't think too much about it and we usually prefer to keep it simple than to apply too much magic.
In my opinion since mapping it properly could be problematic in practice, it's highly preferable that the API user is aware of how it works.

Quote:
Maybe for performance implications?

Yes, a bit; especially if you plan to involve reflection but also since the String format chosen for encoding should be as small as possible and so the representation depends on the content of the fields, and we can't guess the content from the type alone.

If you want to try creating a patch to implement such a feature, that could be a nice contribution to the code base, provided you add a couple of unit tests using a non-trivial composition to proof the flexibility of the idea.
Performance doesn't need to be a dominant factor in the design as people will always be able to override the default if needed, and would only need to care about in case the performance was really an issue, but I would dislike such a feature if it was going to fail in many common cases.

_________________
Sanne
http://in.relation.to/


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