9 Replies Latest reply on Nov 17, 2005 6:26 AM by Emmanuel Bernard

    @Embedded/@Embeddable with more complex classes?

    Ulf Niesar Newbie

      Hi guys,

      I'm still in the process to understand what EJB 3.0 can do for me. Right now I'm fighting with a little more complex Java Object I would like to embed in an Entity Bean.

      The POJO that should be embedded looks like this

      public GeoPoint {
      private Latitude latitude;
      private Longitude longitude;
      }

      Latitude and Longitude don't have any private variables and are inherited from SphericalCoordinate
      public Latitude extends SphericalCoordinate // no data, just a bunch of methods
      public Longitude extends SphericalCoordinate // same

      public SphericalCoordinate {
      private double degrees;
      }

      OK, now I want to embed a GeoPoint in an entity bean. So I do the following in that entity bean:
      @Embedded
      public GeoPoint getGeoPoint() {..}
      [class GeoPoint has been tagged with annotation @Embeddable(access = AccessType.FIELD)]

      The result is quite promising. I get a table created with something like
      CREATE TABLE ... LATITUDE VARBIN(255), LONGITUDE VARBIN(255), ...


      Now the question is how to get something like
      CREATE TABLE ... LATITUDE DOUBLE, LONGITUDE DOUBLE, ... ???

      I'm asking this silly question since if I'm doing the obvious I'm getting a completely unexpected result.
      The code looks like this:

      @Embeddable(assess=AccessType.FIELD)
      public GeoPoint {
      private Latitude latitude;
      private Longitude longitude;
      
      @Embedded public Latitude getLatitude() {..}
      
      @Embedded public Longitude getLongitude() {..}
      
      [...bunch of methods...]
      }
      
      @Embeddable
      public Longitude extends SphericalCoordinate {
      [...bunch of methods...]
      }
      
      @Embeddable(assess=AccessType.FIELD)
      public SphericalCoordinate {
      private double degrees;
      
      public double getDegrees() {..}
      [...bunch of methods...]
      }
      


      Now guess what happens ;)
      The automatically created table contains no sign of any GeoPoint, Latitude, Longitude or SphericalCoordinate. Simply non existent. And best of all, I can't find any log message telling me anything (although Hibernate is quite verbose otherwise)


      So what is going on here?
      A. Am I missunderstanding something?
      B. Is the problem that there are no variables in Latitude and Longitude, so Hibernate thinks there is nothing to store anyway, so disregard it?
      C. Am I in trouble because Latitude and Longitude are both inherited from SphericalCoordinate and SphericalCoordinate has the private variable called degrees? In fact I don't know how to use @AttributeOverrides/@AttributeOverride in Latitude and Longitude since there is nothing to tag with these annotations. (Originally, I wanted to check out the automatically generated names in order to use @AttributeOverrides/@AttributeOverride within GeoPoint)
      D: Is it a bug?
      E. Something different?


      I would really appreciate if somebody could enlight me what my problem is. I really don't see it.

        • 1. Re: @Embedded/@Embeddable with more complex classes?
          Ulf Niesar Newbie

          Hmm, no possibility to edit a post?

          Anyway, there was a typo. I meant
          @Embeddable(access=AccessType.FIELD)
          instead of:
          @Embeddable(assess=AccessType.FIELD)

          So the problem must be something different... ;)

          • 2. Re: @Embedded/@Embeddable with more complex classes?
            Ulf Niesar Newbie

            OK, I found a "solution." The problem was the inheritance.

            This is stupid code, but it works:

            @Embeddable(access=AccessType.FIELD)
            public GeoPoint {
            private Latitude latitude;
            private Longitude longitude;
            
            @Embedded public Latitude getLatitude() {..}
            
            @Embedded public Longitude getLongitude() {..}
            
            [...bunch of methods...]
            }
            
            @Embeddable(access=AccessType.PROPERTY)
            public Longitude extends SphericalCoordinate {
            
            // need to include this for @Embeddable
            public double getLatitude() {
             return super.getDegrees();
            }
            
            // need to include this for @Embeddable
            public void setLatitude(double degrees) {
             super.setDegrees(degrees);
            }
            }
            
            public SphericalCoordinate {
            private double degrees;
            
            public double getDegrees() {..}
            public void setDegrees(double degrees) {..}
            }
            

            This actually builds the desired table automatically:
            CREATE TABLE ... LATITUDE DOUBLE, LONGITUDE DOUBLE, ...

            But honestly, this code looks rather like a workaround than a clean solution.

            The problem was that Latitude and Longitude are inherited from SphericalCoordinate. Unfortunately it looks like EJB 3.0 is not smart enough to handle data in superclasses. So if you switch to AccessType.PROPERTY and include some additional getter and setter methods in Latitude and Longitude, EJB 3.0 understands that there are more data which need to be handled.

            In case there is no more elegant solution it appears to me there are 2 problems:

            (1) Im my opinion it can't be acceptable that EJB 3.0 ignores the data in super classes and you have to find crazy workarounds to get the data stored. It should actually look for such cases by itself!
            The following should work:
            @Embeddable(access=AccessType.FIELD)
            public Longitude extends SphericalCoordinate {
            ...
            }
            
            @Embeddable(access=AccessType.FIELD)
            public SphericalCoordinate {
            private double degrees;
            ...
            }


            (2) If you name the getter/setter methods in Latitude/Longitude for the data in the superclass:
            double getDegrees()
            void setDegrees(double value)
            you have a more meaningful code. (Well, it doesn't make too much sense since they both exist in the superclass anyway, but at least that doesn't look completely wrong from the programmer's point of view.)
            BUT if you do it this way you are running into an error and the par file won't load. In this case there are two identical column names created ("degrees") and you end up in a bunch of exceptions when the par or ear file is deployed. In my opinion, the system should really be smart enough to build different names for the columns in such a case.


            Please let me know if there are better ways to handle such a case.
            Otherwise I think (1) should become a feature request in the JIRA and (2) looks like bug to me, but could also be considred as a feature request.

            • 3. Re: @Embedded/@Embeddable with more complex classes?
              Emmanuel Bernard Master

              (0) when using @Embeddable(access=AccessType.FIELD), you must annotate the fields and not the getters. your code cannot work.
              (1) is already implemented
              (2) you cannot have twice the same column name in a table => use @AttributeOverride

              • 4. Re: @Embedded/@Embeddable with more complex classes?
                Ulf Niesar Newbie

                Thanks for your answer, Emmanuel!

                "epbernard" wrote:
                (0) when using @Embeddable(access=AccessType.FIELD), you must annotate the fields and not the getters. your code cannot work.

                You know, if programs don't work as expected you sometimes start to try weird things. So I ended up mixing the AccessType.FIELD and AccessType.PROPERTY stuff in a kind of desperation. Unfortunately that was one of my first cases I moved to EJB 3.0 and got into trouble right away ;)) Meanwhile I

                "epbernard" wrote:
                (1) is already implemented

                Hmm, really?
                Well, let me show you what I expected to work originally. Everything is heavily stripped, of cource..

                Bean:
                @Entity(access=AccessType.FIELD)
                @Table (name = "GeoPointTest1")
                public class Test implements java.io.Serializable {
                
                 @Id
                 private String name;
                
                 @Embedded
                 private GeoPoint geoPoint;
                
                 public String getName() {
                 return name;
                 }
                 public GeoPoint getGeoPoint() {
                 return geoPoint;
                 }
                }

                Embedded classes:
                @Embeddable(access = AccessType.FIELD)
                public class GeoPoint
                implements java.io.Serializable {
                
                 @Embedded
                 @AttributeOverrides({
                 @AttributeOverride(name = "degrees", column = @Column(name = "latitude"))
                 })
                 private Latitude latitude;
                
                 @Embedded
                 @AttributeOverrides({
                 @AttributeOverride(name = "degrees", column = @Column(name = "longitude"))
                 })
                 private Longitude longitude;
                
                 public GeoPoint() {
                 latitude = new Latitude();
                 longitude = new Longitude();
                 }
                
                 public Latitude getLatitude() {
                 return (latitude);
                 }
                 public Longitude getLongitude() {
                 return (longitude);
                 }
                
                }

                @Embeddable(access = AccessType.FIELD)
                public class Latitude
                extends SphericalCoordinate
                implements java.io.Serializable {
                
                 public Latitude() {
                 super();
                 }
                 // several methods...
                }

                @Embeddable(access = AccessType.FIELD)
                public class Longitude
                extends SphericalCoordinate
                implements java.io.Serializable {
                
                 public Longitude() {
                 super();
                 }
                 // several methods...
                }

                @Embeddable(access = AccessType.FIELD)
                public class SphericalCoordinate
                implements java.io.Serializable {
                
                 @Basic
                 private double degrees;
                
                 public SphericalCoordinate() {
                 degrees = 0.0;
                 }
                 public double getDegrees() {
                 return(degrees);
                 }
                }


                This is the automatically created table:
                create table GeoPointTest1 (name varchar(255) not null, primary key (name))

                No degrees, no latitude, no longitude - nothing. (JBoss-4.0.3SP1 with jboss-EJB-3.0_RC3.zip)

                Where is the bug???


                • 5. Re: @Embedded/@Embeddable with more complex classes?
                  Emmanuel Bernard Master

                   

                  @EmbeddableSuperclass(access = AccessType.FIELD)
                  public class SphericalCoordinate


                  • 6. Re: @Embedded/@Embeddable with more complex classes?
                    Ulf Niesar Newbie

                    Hi Emmanuel,

                    thanks again for taking a look into this isuue. I really appreciate!

                    "epbernard" wrote:
                    @EmbeddableSuperclass(access = AccessType.FIELD)
                    public class SphericalCoordinate

                    Sorry, I missed that when I posted the code. It is correct in my non-working class example.

                    Correction:
                    @EmbeddableSuperclass(access = AccessType.FIELD)
                    public class SphericalCoordinate
                    implements java.io.Serializable {
                    
                     @Basic
                     private double degrees;
                    
                     public SphericalCoordinate() {
                     degrees = 0.0;
                     }
                     public double getDegrees() {
                     return(degrees);
                     }
                    }
                    


                    This isn't the point. I honestly suspect there is either something wrong with the variable-in-the-superclass stuff or with the @AttributeOverride (in class GeoPoint).

                    • 7. Re: @Embedded/@Embeddable with more complex classes?
                      Ulf Niesar Newbie

                      OK, I found the problem.
                      There was a typo in my @AttributeOverride. Too bad there are no checks available (yet?) for all this in eclipse. You really get used to all these little helpers...

                      Thanks for your help!

                      • 8. Re: @Embedded/@Embeddable with more complex classes?
                        Ulf Niesar Newbie

                        I would like to apologize again for the discrepancies between the code I posted here and the one which I'm actually running. The explanation is that Internet access is extremely restricted on my development system and I have to use a different computer to access this forum. So I couldn't cut and paste but had to rewrite all the code by hand. This way I made mistakes which are not there in the "real" code and overlooked the typo that caused all the trouble. Sorry about that!

                        Anyway, let me finish this issue with a hopefully simple question. I really don't have an idea yet how to accomplish this:

                        In the entity bean in which my little trouble maker GeoPoint is embedded, I would like to use something like
                        @Embedded(nullable=true) // access=AccessType.FIELD is used
                        GeoPoint aGeoPoint


                        You know, just like
                        @Column(nullable=true)

                        What I mean is that it should be possible that the GeoPoint variable in the entity bean can be null. Unfortunately I need that in some cases.

                        Right now I'm getting a table like this

                        create table test1 LocationId integer generated by default as identity (start with 1), [...], latitude double not null, longitude double not null, [...]


                        I aleady tried to achieve that within GeoPoint, but no success:
                        @Embeddable(access = AccessType.FIELD)
                        public class GeoPoint
                        implements java.io.Serializable {
                        
                         @Embedded
                         @AttributeOverrides({
                         @AttributeOverride(name = "degrees", column = @Column(name = "latitude", nullable=true))
                         })
                         private Latitude latitude;
                        
                         @Embedded
                         @AttributeOverrides({
                         @AttributeOverride(name = "degrees", column = @Column(name = "longitude", nullable=true))
                         })
                         private Longitude longitude;
                        
                         [...]
                        }

                        But that wouldn't be the prefered solution anyway. GeoPoint is used in other beans where the GeoPoint variable must not be null. In those cases I'm quite happy about the not null restriction.

                        But how do I allow the GeoPoint varable be be null in an entity bean where I need that?

                        • 9. Re: @Embedded/@Embeddable with more complex classes?
                          Emmanuel Bernard Master

                          the only way is to use @attributeoverride and override the column definition of the embedded object columns.