4 Replies Latest reply on Jan 12, 2006 9:41 PM by jsb

    Mapping a Map Collection

    jsb

      Can someone explain how mapping a Map collection of entities within an entity works and how it should be done? Or perhaps point me to documentation on the subject? So far, I've found only this:
      http://www.hibernate.org/hib_docs/annotations/reference/en/html/entity.html#entity-mapping-association-collections
      ...which looks promising, but I'm not sure how to set up my entities or if it is intended to work how I am thinking it will.

      Background on what I'm trying to do: I have entities that have n properties that I am storing as key/value pairs. Currently an entity has a collection of properties stored in a Set. Something like this...

      @Entity
      public class ItemA {
       private long id;
       private Set<Property> properties;
      
       @Id(generate = GeneratorType.AUTO)
       public long getId() {
       return id;
       }
       public void setId(long id) {
       this.id = id;
       }
      @OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
       @JoinTable(
       table = @Table(name = "ItemA_Properties"),
       joinColumns = @JoinColumn(name = "Property_id", referencedColumnName = "id"),
       inverseJoinColumns = @JoinColumn(name = "ItemA_id", referencedColumnName = "id")
       )
       public Set<Property> getProperties() {
       return properties;
       }
       public void setProperties(Set<Property> properties) {
       this.properties = properties;
       }
      }
      
      @Entity
      public class Property {
       private long id;
       private String propKey;
       private String propValue;
      
       @Id(generate = GeneratorType.AUTO)
       public long getId() {
       return id;
       }
       public void setId(long id) {
       this.id = id;
       }
       public String getPropKey() {
       return propKey;
       }
       public void setPropKey(String propKey) {
       this.propKey = propKey;
       }
       public String getPropValue() {
       return propValue;
       }
       public void setPropValue(String propValue) {
       this.propValue = propValue;
       }
      }


      This works simply enough, but what would make more sense is if the "properties" collection was a Map. Simply changing the "properties" collection to a "Map<String, Properties>" and setting a "@MapKey(name="propKey")" annotation doesn't seem to be the answer. I get the feeling I'm missing something obvious.

      Thanks for any help on mapping Maps that can be offered,
      Jonn

        • 1. Re: Mapping a Map Collection
          epbernard

          this should work, what's wrong?

          • 2. Re: Mapping a Map Collection
            jsb

            Sorry for the delay in response...

            If my entities look something like this:

            @Entity
            public class ItemA {
             private long id;
             private Map<String, Property> properties;
            
             @Id(generate = GeneratorType.AUTO)
             public long getId() {
             return id;
             }
             public void setId(long id) {
             this.id = id;
             }
            @OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
             @JoinTable(
             table = @Table(name = "ItemA_Properties"),
             joinColumns = @JoinColumn(name = "ItemA_id", referencedColumnName = "id"),
             inverseJoinColumns = @JoinColumn(name = "Property_id", referencedColumnName = "id")
             )
             public Map<String, Property> getProperties() {
             return properties;
             }
             public void setProperties(Map<String, Property> properties) {
             this.properties = properties;
             }
            }
            
            @Entity
            public class Property {
             private long id;
             private String propKey;
             private String propValue;
            
             @Id(generate = GeneratorType.AUTO)
             public long getId() {
             return id;
             }
             public void setId(long id) {
             this.id = id;
             }
             public String getPropKey() {
             return propKey;
             }
             public void setPropKey(String propKey) {
             this.propKey = propKey;
             }
             public String getPropValue() {
             return propValue;
             }
             public void setPropValue(String propValue) {
             this.propValue = propValue;
             }
            }

            ...I seem to be able to save ItemA objects with Maps of properties. But when I try to retreive the ItemA objects, I get these errors:
            2006-01-10 00:28:32,703 DEBUG [com.example.test.ejb.ItemManagerBean] getItems: called
            2006-01-10 00:28:32,718 WARN [org.hibernate.util.JDBCExceptionReporter] SQL Error: 0, SQLState: 42703
            2006-01-10 00:28:32,718 ERROR [org.hibernate.util.JDBCExceptionReporter] ERROR: column properties0_.propkey does not exist

            ...followed by an exception stack that includes this:
            Caused by: javax.ejb.EJBTransactionRolledbackException: null; CausedByException is:
             could not initialize a collection: [com.example.test.par.ItemA.properties#1]
             at org.jboss.ejb3.tx.Ejb3TxPolicy.handleInCallerTx(Ejb3TxPolicy.java:65)
             at org.jboss.aspects.tx.TxPolicy.invokeInCallerTx(TxPolicy.java:117)
             at org.jboss.aspects.tx.TxInterceptor$Required.invoke(TxInterceptor.java:138)
             at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:98)
             at org.jboss.aspects.tx.TxPropagationInterceptor.invoke(TxPropagationInterceptor.java:61)
             at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:98)
             at org.jboss.ejb3.stateless.StatelessInstanceInterceptor.invoke(StatelessInstanceInterceptor.java:39)
             at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:98)
             at org.jboss.aspects.security.AuthenticationInterceptor.invoke(AuthenticationInterceptor.java:63)
             at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:98)
             at org.jboss.ejb3.ENCPropagationInterceptor.invoke(ENCPropagationInterceptor.java:32)
             at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:98)
             at org.jboss.ejb3.asynchronous.AsynchronousInterceptor.invoke(AsynchronousInterceptor.java:91)
             at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:98)
             at org.jboss.ejb3.stateless.StatelessContainer.dynamicInvoke(StatelessContainer.java:204)
             at org.jboss.aop.Dispatcher.invoke(Dispatcher.java:107)
             at org.jboss.ejb3.remoting.IsLocalInterceptor.invoke(IsLocalInterceptor.java:37)
             at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:98)
             at org.jboss.ejb3.stateless.StatelessRemoteProxy.invoke(StatelessRemoteProxy.java:88)
             at $Proxy105.getItems(Unknown Source)
             at com.example.test.ejb.TestServiceBean.testItemManager(TestServiceBean.java:69)
             at com.example.test.ejb.TestServiceBean.testItem(TestServiceBean.java:101)
             at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
             at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
             at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
             at java.lang.reflect.Method.invoke(Method.java:585)
             at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:109)
             at org.jboss.ejb3.AllowedOperationsInterceptor.invoke(AllowedOperationsInterceptor.java:32)
             at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:98)
             at org.jboss.aspects.tx.TxPolicy.invokeInOurTx(TxPolicy.java:66)
             at org.jboss.aspects.tx.TxInterceptor$Required.invoke(TxInterceptor.java:134)
             at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:98)
             at org.jboss.aspects.tx.TxPropagationInterceptor.invoke(TxPropagationInterceptor.java:61)
             at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:98)
             at org.jboss.aspects.security.AuthenticationInterceptor.invoke(AuthenticationInterceptor.java:63)
             at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:98)
             at org.jboss.ejb3.ENCPropagationInterceptor.invoke(ENCPropagationInterceptor.java:32)
             at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:98)
             at org.jboss.ejb3.asynchronous.AsynchronousInterceptor.invoke(AsynchronousInterceptor.java:91)
             at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:98)
             at org.jboss.ejb3.service.ServiceContainer.localInvoke(ServiceContainer.java:151)
             at org.jboss.ejb3.service.ServiceLocalProxy.invoke(ServiceLocalProxy.java:57)
             at $Proxy112.testItem(Unknown Source)
             at com.example.test.web.ItemBean.getItemTest(ItemBean.java:32)
             ... 58 more

            Notice anything I could be doing wrong?

            • 3. Re: Mapping a Map Collection
              jsb

              Oops! I forgot to include the @MapKey(name="propKey") in my ItemA example entity. I do have this in place in the code I am testing.

              • 4. Re: Mapping a Map Collection
                jsb

                This looks like the same issue:
                http://opensource2.atlassian.com/projects/hibernate/browse/ANN-120

                I was able to get it to work by switching to the @JoinColumn relational strategy, but in my actual project I am using the Property entities with multiple owning entities so join table per owning entity makes more sense.