Creating a copy of a partially loaded Object

1,111 views
Skip to first unread message

Daniel Atallah

unread,
Apr 26, 2012, 6:41:11 PM4/26/12
to eb...@googlegroups.com
Folks,

I have a situation where I need to make a copies of Objects that use lazy loading (via dynamic subclassing) and may be partially loaded.

I've tried a number of strategies, but so far haven't been particularly happy with any of them.

The challenge is that unless i disable lazy loading or tell the use setVanillaMode(true) on the query, any properties that haven't yet been accessed to trigger lazy loading will be lost regardless of how I do the copy.

I've tried using EntityBean._ebean_createCopy(), Object.clone(), and Serializing to copy the object using org.apache.commons.lang.SerializationUtils.clone(), but the behavior with all of these is essentially the same; any lazy loading that hasn't yet been done will not be done prior to copying.

I've spent some time searching the group archives and found a number of threads that sound similar, but didn't see any solutions that are optimal.

The workarounds that seem to work are disabling lazy loading by one of the two methods described above (which if possible, i'd like to avoid because the laziness is useful in some cases), to explicitly specify which properties to load when retrieving the object, or to call the getter method causing the lazy loading to be triggered.

Is there a utility method of some variety that I can use to force the immediate loading of all unloaded lazy properties?

I'm also wondering if there is an intentional reason that lazy properties aren't all loaded when the Serialization process converts the subclass to the vanilla object (SerializeControl.isVanillaBeans())?
I've tried to think of reasons why it would be desirable for it to work this way and the only thing that I can think of is performance.
It seems like the behavior at the moment is "wrong" - when serializing/deserializing an object, I would expect an object to be produced that had the same properties set as the parent object (I realize that there are exceptions to this - Objects are free to do sneaky stuff during serialization, but generally it's a bad idea unless there is a good reason to do so).

Thanks!
-Daniel




Dominik Dorn

unread,
Apr 27, 2012, 7:51:42 AM4/27/12
to eb...@googlegroups.com
If I remember the documentation correctly, accessing a property that
hasn't been loaded
before will load the whole object. So basically just accessing one of
the lazy loaded properties
prior to serialization/cloning/whatever should do the trick.
Afaik, this does not apply to stuff annotated with @Lob, so you'd have
to manually access that
before serialization.

hope that helps.

dominik
--
Dominik Dorn
http://dominikdorn.com
http://twitter.com/domdorn

Skripten, Mitschriften, Lernunterlagen, etc. findest Du auf
http://www.studyguru.eu !

Daniel Atallah

unread,
Apr 27, 2012, 11:04:57 AM4/27/12
to eb...@googlegroups.com


On Friday, 27 April 2012 07:51:42 UTC-4, Dominik Dorn wrote:
If I remember the documentation correctly, accessing a property that
hasn't been loaded
before will load the whole object. So basically just accessing one of
the lazy loaded properties
prior to serialization/cloning/whatever should do the trick.
Afaik, this does not apply to stuff annotated with @Lob, so you'd have
to manually access that
before serialization.

Yeah, this works (and is what I've been doing), but I was hoping for a more elegant solution- I'm trying to create a method in a base class that creates the copy.
I can't tell which property hasn't been lazy loaded yet, so I have to access all of them.

This is what I've got:
    /**
     *  Create a copy of this object using serialization
     *  This will strip off the EBean magic and explicitly preload any lazy loaded properties
     */
    @Transient
    public EltModelBase createCopy() {
        //Make sure that any lazy properties are loaded
        // Find the base entity class
        Class<? extends EltModelBase> clazz = this.getClass();
        //Find the enclosing class (for deal with Ebean dynamic subclasses)
        while (clazz.getName().indexOf('$') != -1) {
            @SuppressWarnings("unchecked")
            Class<? extends EltModelBase> superclass = (Class<? extends EltModelBase>) clazz.getSuperclass();
            clazz = superclass;
        }
        for (Method m : clazz.getMethods()) {
            if (m.getReturnType() != null && m.getParameterTypes().length == 0 && !m.isAnnotationPresent(Transient.class) && !Object.class.equals(m.getDeclaringClass())) {
                try {
                    //Invoke the subclass method that will deal with lazy-loading
                    clazz.getMethod(m.getName()).invoke(this);
                } catch (Exception e) {
                    log.error(String.format("Error preloading lazy properties prior to cloning. Method: %s.%s", clazz.getName(), m.getName()), e);
                }
            }
        }

        return (EltModelBase) SerializationUtils.clone(this);
    }


Daryl Stultz

unread,
Apr 27, 2012, 11:33:55 AM4/27/12
to eb...@googlegroups.com
On Fri, Apr 27, 2012 at 11:04 AM, Daniel Atallah <daniel....@gmail.com> wrote:


On Friday, 27 April 2012 07:51:42 UTC-4, Dominik Dorn wrote:
If I remember the documentation correctly, accessing a property that
hasn't been loaded
before will load the whole object.
Yeah, this works (and is what I've been doing), but I was hoping for a more elegant solution- I'm trying to create a method in a base class that creates the copy.
I can't tell which property hasn't been lazy loaded yet, so I have to access all of them.

I don't use subclassing at all but you might try to cast your bean to EntityBean. Then you can call _ebean_getFieldNames() to get all the field names. You can also get the EntityBeanIntercept and find out what ones have been loaded. It might be sufficient to loop over the field names and call _ebean_getFieldIntercept to trigger lazy loading.

Be sure to read the JavaDocs on this stuff.

/Daryl

Daniel Atallah

unread,
Apr 27, 2012, 3:09:26 PM4/27/12
to eb...@googlegroups.com


On Friday, 27 April 2012 11:33:55 UTC-4, Daryl Stultz wrote:

Thanks for the suggestion, I was able to implement the following, which is cleaner than my reflection based version:


    /**
     *  Create a copy of this object using serialization
     *  This will strip off the EBean magic and explicitly preload any lazy loaded properties
     */
    @Transient
    public Object createCopy() {

        if (ArrayUtils.contains(this.getClass().getInterfaces(), EntityBean.class)) {
            //If this is a dynamic ebean subclass, make sure that any lazy properties are loaded
            EntityBean eb = (EntityBean) this;
            List<String> fieldNames = Arrays.asList(eb._ebean_getFieldNames());
            EntityBeanIntercept eb_intercept = eb._ebean_intercept();
            Set<String> loadedProps = eb_intercept.getLoadedProps();
            if (loadedProps != null && !loadedProps.containsAll(fieldNames)) {
                for (int i = 0; i < fieldNames.size(); i++) {
                    String fieldName = fieldNames.get(i);
                    if (!loadedProps.contains(fieldName)) {
                        eb._ebean_getFieldIntercept(i, this);
                    }
                }
            }
        }

        return SerializationUtils.clone(this);
    }

I still have the question about whether or not it would be more correct to do this automatically during Serialization to a Vanilla Object.

If I were to submit a patch to make the Vanilla Serialization do this, would it be accepted?

Thanks,
-D

Rob Bygrave

unread,
Apr 28, 2012, 4:32:47 AM4/28/12
to eb...@googlegroups.com
>> If I were to submit a patch to make the Vanilla Serialization do this, would it be accepted

There would be a few issues that need addressing.
- How does it work in terms of object graph depth
- Does it maintain the current behavior for all the existing users

Daniel Atallah

unread,
Apr 28, 2012, 2:23:46 PM4/28/12
to eb...@googlegroups.com


On Saturday, 28 April 2012 04:32:47 UTC-4, Rob Bygrave wrote:
>> If I were to submit a patch to make the Vanilla Serialization do this, would it be accepted

There would be a few issues that need addressing.
- How does it work in terms of object graph depth

There are a couple different possibilities for this.
On one hand, just looking at the POJO object, one might (reasonably?) expect that the lazy loading would be done "all the way" and you'd end up with an Object on the other side of the Serialization boundary that (from an API perspective) behaves the same way as the dynamic subclass.
Clearly, this is potentially a big problem for certain object structures, so it may not be a reasonable default behavior.

On the other hand, it is probably not unreasonable that the Serialization behavior of dynamically subclassed entities would be different than "normal" java serialization for POJOs - the fact that lazy loading is configurable is probably the first hint that something may be different, so as long as it is documented (and there are (hopefully simple) things that you can do to make things work as you need them to), any behavior is probably ok.

So, here's my first pass thought about how it might work:
  • Annotations at the field or class level could control the lazy loading depth
    • e.g. @SerializationLazyLoad(Exclude|Include|Recurse)
      • Exclude - Essentially do what is done today; no Lazy Loading
      • Include -  Lazily load any properties in the root object being serialized, but don't do lazy loading on nested objects (this is essentially what my createCopy() method above does)
      • Recurse - Lazily load any properties in the root object and in any nested objects down the graph

An Object having lazy loading done at the "Include" level would cause the "Exclude" level to be used for nested properties.

I'm not sure how easy this would be easy to fit seamlessly into the java serialization model because it might be hard to tell if something is the "root" object - it might be possible to use a transient field to pass down the information during the serialization process.

If it ends up being impractical to integrate with the java serialization process, it seems like it'd be at least possible to have something like a Ebean.toVanillaObject(Object) method that could do this.

- Does it maintain the current behavior for all the existing users

That depends on what the default behavior is; if it is "Exclude", then the behavior wouldn't be changed.

Based on the searching that I did when first having issues and my perception about how stuff works, it seems like anyone who wants to use serialization and uses lazy loading would have had to come up with some sort of workaround for this issue, so perhaps it wouldn't be a bad thing if the default behavior were to change.

I think the argument could be made that "Recurse" should be the default; but I probably don't know enough about other use cases to make a good recommendation.  My guess is that, based on the way it works, most people don't send dynamic subclass based Entities over a Serialization boundaries.
This would likely be a setting that is definable in ebean.properties.

Thoughts?
-D

Rob Bygrave

unread,
May 3, 2012, 5:54:52 PM5/3/12
to eb...@googlegroups.com
As a 'left field' thought ... have you considered other options like JSON, XML etc. 

That is, when you serialise an ORM object graph you will often pull in a lot of relationships and serialise a lot more than you need / desire. Serializing Order ... Order + Order Details + Product + Customer + Customer Contacts ...

If you serialize using JSON say then you can choose exactly what parts of the object graph are included.

If we want to have that sort of control with Object serialisation then we'd also need to replace referred beans with 'reference beans' ... so before you serialize a order bean the customer bean is actually replaced with a reference bean (so that a full customer bean doesn't get serialised effectively cascading to all its related beans etc).

Hmmm.

Daniel Atallah

unread,
May 3, 2012, 7:38:35 PM5/3/12
to eb...@googlegroups.com


On Thursday, 3 May 2012 17:54:52 UTC-4, Rob Bygrave wrote:
As a 'left field' thought ... have you considered other options like JSON, XML etc. 


That is, when you serialise an ORM object graph you will often pull in a lot of relationships and serialise a lot more than you need / desire. Serializing Order ... Order + Order Details + Product + Customer + Customer Contacts

Yes, I certainly see how this could be an issue for full serialization.
 
...

If you serialize using JSON say then you can choose exactly what parts of the object graph are included.

If we want to have that sort of control with Object serialisation then we'd also need to replace referred beans with 'reference beans' ... so before you serialize a order bean the customer bean is actually replaced with a reference bean (so that a full customer bean doesn't get serialised effectively cascading to all its related beans etc).

Hmmm.

Perhaps the intent of my idea wasn't clear.

Just to bring back the original context, this started off because I wanted to make a copy of an Object, not specifically because I needed to Serialize the Object (I just tried Serialization during my travels and was surprised by the behavior (perhaps I shouldn't have been surprised)).
I was able to get my copying functionality working adequately, but during the process it occurred to me that this is a bigger issue in that Serialization is "broken" (for some definition at least), and that's what the followup here is about.

I recognize that there is always the ability to manage Serialization manually for any given Object; using the JSON or XML writers and parsers as a method for implementing that may be a reasonable thing to do, but my proposal is more about trying to come up with a way to have consistently serialized Objects without implementing custom handling each time, rather a recipe for implementing serialization for any specific object.

I'm not sure when I'd have time to work on such an implementation, but I figured I'd at least explore some ideas on what could be done to improve the serialization behavior.
If I'm mistaken that there is a need for such a thing, fair enough.

-D
 
 

Rob Bygrave

unread,
May 3, 2012, 11:21:17 PM5/3/12
to eb...@googlegroups.com
>> I wanted to make a copy of an Object

It would be good to understand the purpose of the copy. Typically I'd expect people to use serialization to transport the object graph or persist it to a file system.

I believe some people use a 'vanilla' query so as to get back 'vanilla' object graph (so not Ebean generated subclasses or enhanced etc) for the purpose of serialization and sending over the wire.

>> bigger issue in that Serialization is "broken" (for some definition at least), ...

So yes Ebean serialized the bean as is (partially loaded etc) ... and you were expecting it to fully load the bean invoking lazy loading as necessary (at least if you were serializing to vanilla beans). The current behavior works for some use cases but not all and not your one.

What we don't want is the default behavior to be ... 'serialize an order bean and half the database gets lazy loaded'.

Anyway, quite a bit to think about with this issue.

Durchholz, Joachim

unread,
May 4, 2012, 7:00:01 AM5/4/12
to eb...@googlegroups.com
> I recognize that there is always the ability to manage
> Serialization manually for any given Object; using the
> JSON or XML writers and parsers as a method for
> implementing that may be a reasonable thing to do, but
> my proposal is more about trying to come up with a way
> to have consistently serialized Objects without
> implementing custom handling each time, rather a recipe
> for implementing serialization for any specific object.

How would you specify what parts of the object path to serialize and what parts to leave out?

Rob Bygrave

unread,
May 4, 2012, 8:05:18 AM5/4/12
to eb...@googlegroups.com
>> How would you specify what parts of the object path to serialize and what parts to leave out?

With Ebean's JSON support you use JsonWriteOptions.parsePath() ... and for some background this exists because it is useful for use with JAX-RS. It was inspired by the LinkedIn API and you can see a presentation on this  ...
http://www.slideshare.net/linkedin/building-consistent-restful-apis-in-a-highperformance-environment

... and in terms of Ebean and JAX-RS you can read something here:
http://www.avaje.org/ebean/jaxrs.html


Some example code is:




        // fetch back a list or a map of beans ...  sometimes maps are useful to return in json

        Map<String, Customer> map = Ebean.find(Customer.class)
            .findMap("id", String.class);
               
        JsonContext jsonContext = Ebean.createJsonContext();

        // specify which property
        JsonWriteOptions jsonWriteOptions = JsonWriteOptions.parsePath("(id,status,name)");

        String jsonString = jsonContext.toJsonString(map, true, jsonWriteOptions);
        System.out.println(jsonString);

...

or a more complex example ...

    // some of the customer properties and all the billingAddress
    jsonWriteOptions = JsonWriteOptions.parsePath("(id,status,billingAddress(*))");


and a more complex example ...

    // some of the customer properties ... some of shippingAddress, all of the billingAddress and all of the contacts
    jsonWriteOptions = JsonWriteOptions.parsePath("(id,status,name,shippingAddress(id,line1,city),billingAddress(*),contacts(*))");


... and in doing this email I hit a bug http://www.avaje.org/bugdetail-398.html ... and just fixed it in HEAD.


Cheers, Rob.


Oh ... and the json output is ...



{"id":1,"status":"NEW","name":"Rob"},
"2":
{"id":2,"status":"NEW","name":"Cust NoAddress"},
"3":
{"id":3,"status":"ACTIVE","name":"Fiona"},
"4":
{"id":4,"status":"ACTIVE","name":"NocCust"}}



// ----------------------- 2nd examples -------------------------------

{"1":
{"id":1,"status":"NEW",
    "billingAddress":{"id":1,"line1":"P.O.Box 1234","line2":"St Lukes","city":"Auckland","cretime":null,
        "country":{"code":"NZ"},"updtime":"2012-05-04T11:40:35.333Z"}},
"2":
{"id":2,"status":"NEW",
    "billingAddress":null},
"3":
{"id":3,"status":"ACTIVE",
    "billingAddress":{"id":3,"line1":"West Coast Rd","line2":"St Lukes","city":"Auckland","cretime":null,
        "country":{"code":"NZ"},"updtime":"2012-05-04T11:40:35.343Z"}},
"4":
{"id":4,"status":"ACTIVE",
    "billingAddress":{"id":5,"line1":"Bos town","line2":"St Lukes","city":"Auckland","cretime":null,
        "country":{"code":"NZ"},"updtime":"2012-05-04T11:40:35.346Z"}}}


// ----------------------- 3rd examples -------------------------------


{"1":
{"id":1,"status":"NEW","name":"Rob",
    "shippingAddress":{"id":2,"line1":"1 Banana St","city":"Auckland"},
    "billingAddress":{"id":1,"line1":"P.O.Box 1234","line2":"St Lukes","city":"Auckland","cretime":null,
        "country":{"code":"NZ"},"updtime":"2012-05-04T12:03:10.703Z"},
    "contacts":[
        {"id":1,"firstName":"Jim1","lastName":"Cricket","phone":null,"mobile":null,"email":null,
            "group":null,"cretime":"2012-05-04T12:03:10.710Z","updtime":"2012-05-04T12:03:10.710Z"},
        {"id":2,"firstName":"Fred1","lastName":"Blue","phone":null,"mobile":null,"email":null,
            "group":null,"cretime":"2012-05-04T12:03:10.710Z","updtime":"2012-05-04T12:03:10.710Z"},
        {"id":3,"firstName":"Bugs1","lastName":"Bunny","phone":null,"mobile":null,"email":null,
            "group":null,"cretime":"2012-05-04T12:03:10.714Z","updtime":"2012-05-04T12:03:10.714Z"}
    ]},
"2":
{"id":2,"status":"NEW","name":"Cust NoAddress",
    "shippingAddress":null,
    "billingAddress":null,
    "contacts":[
        {"id":4,"firstName":"Jack","lastName":"Black","phone":null,"mobile":null,"email":"bla...@test.com",
            "group":null,"cretime":"2012-05-04T12:03:10.714Z","updtime":"2012-05-04T12:03:10.714Z"}
    ]},
"3":
{"id":3,"status":"ACTIVE","name":"Fiona",
    "shippingAddress":{"id":4,"line1":"12 Apple St","city":"Auckland"},
    "billingAddress":{"id":3,"line1":"West Coast Rd","line2":"St Lukes","city":"Auckland","cretime":null,
        "country":{"code":"NZ"},"updtime":"2012-05-04T12:03:10.714Z"},
    "contacts":[
        {"id":5,"firstName":"Jim1","lastName":"Cricket","phone":null,"mobile":null,"email":null,
            "group":null,"cretime":"2012-05-04T12:03:10.715Z","updtime":"2012-05-04T12:03:10.715Z"},
        {"id":6,"firstName":"Fred1","lastName":"Blue","phone":null,"mobile":null,"email":null,
            "group":null,"cretime":"2012-05-04T12:03:10.716Z","updtime":"2012-05-04T12:03:10.716Z"},
        {"id":7,"firstName":"Bugs1","lastName":"Bunny","phone":null,"mobile":null,"email":null,
            "group":null,"cretime":"2012-05-04T12:03:10.716Z","updtime":"2012-05-04T12:03:10.716Z"},
        {"id":8,"firstName":"Fiona","lastName":"Black","phone":null,"mobile":null,"email":"bla...@test.com",
            "group":null,"cretime":"2012-05-04T12:03:10.716Z","updtime":"2012-05-04T12:03:10.716Z"},
        {"id":9,"firstName":"Tracy","lastName":"Red","phone":null,"mobile":null,"email":"re...@test.com",
            "group":null,"cretime":"2012-05-04T12:03:10.716Z","updtime":"2012-05-04T12:03:10.717Z"}
    ]},
"4":
{"id":4,"status":"ACTIVE","name":"NocCust",
    "shippingAddress":{"id":6,"line1":"15 Kumera Way","city":"Auckland"},
    "billingAddress":{"id":5,"line1":"Bos town","line2":"St Lukes","city":"Auckland","cretime":null,
        "country":{"code":"NZ"},"updtime":"2012-05-04T12:03:10.717Z"},
    "contacts":[
        {"id":10,"firstName":"Jim1","lastName":"Cricket","phone":null,"mobile":null,"email":null,
            "group":null,"cretime":"2012-05-04T12:03:10.718Z","updtime":"2012-05-04T12:03:10.718Z"},
        {"id":11,"firstName":"Fred1","lastName":"Blue","phone":null,"mobile":null,"email":null,
            "group":null,"cretime":"2012-05-04T12:03:10.718Z","updtime":"2012-05-04T12:03:10.718Z"},
        {"id":12,"firstName":"Bugs1","lastName":"Bunny","phone":null,"mobile":null,"email":null,
            "group":null,"cretime":"2012-05-04T12:03:10.718Z","updtime":"2012-05-04T12:03:10.718Z"}
    ]}}

Daniel Atallah

unread,
May 4, 2012, 5:13:23 PM5/4/12
to eb...@googlegroups.com


On Thursday, 3 May 2012 23:21:17 UTC-4, Rob Bygrave wrote:
>> I wanted to make a copy of an Object

It would be good to understand the purpose of the copy. Typically I'd expect people to use serialization to transport the object graph or persist it to a file system.

Yes, it's probably a good idea to explain why the copying is happening.

In the application I'm working on, there are a subset of the Objects that are "Versioned".
Essentially, this means that these Objects are immutable in most scenarios.
When the user updates an existing Object that implements the Versioned interface, we intercept the update via BeanPersistController.preUpdate() and instead of allowing the update to go through, we create a copy, apply some additional relationship tracking changes and save the copy instead (it ends up being a pretty neat way to implement the versioning).
The reason I noticed the issue is that @ManyToOne mapped columns may not have been loaded and ended up getting to the DB as NULL (in a non-nullable column).

The copying also occurs during some other operations (e.g. merging), but so far it's all related to versioning process.


What we don't want is the default behavior to be ... 'serialize an order bean and half the database gets lazy loaded'.

Absolutely; no disagreement on that.
 

Rob Bygrave

unread,
May 5, 2012, 7:38:30 PM5/5/12
to eb...@googlegroups.com
My interpretation is that for this functionality you'd like to have a 'shallow copy' function that also ensures that the bean is fully loaded.

So its a 'shallow' copy meaning that any @ManyToOne and @OneToOne objects get replaced by reference beans (so not a cascading copy function).

Does that sound about right?

Daniel Atallah

unread,
May 7, 2012, 10:48:53 AM5/7/12
to eb...@googlegroups.com

On Saturday, 5 May 2012 19:38:30 UTC-4, Rob Bygrave wrote:
My interpretation is that for this functionality you'd like to have a 'shallow copy' function that also ensures that the bean is fully loaded.

Yes, this is the minimum that I needed for my use case (and what the solution that I posted does). 
 
So its a 'shallow' copy meaning that any @ManyToOne and @OneToOne objects get replaced by reference beans (so not a cascading copy function).

I haven't been able to dig up much documentation on what "reference beans" are apart from "isReference()" type of methods in the API.
Are these just "empty" (apart from @Id)  Dynamic subclass implementations that lazy-load everything? 
 
Does that sound about right?

It could be - there have been some cases where I've wanted to have access to "deeper" objects, and currently I've had to explicitly pre-load those (either via Query.fetch(), or by accessing the getter method).  If my understanding on what these reference objects are is correct this would be nice for my use case.


I feel like we're moving away from the Serialization question(which isn't a problem for me currently because I don't actually need to Serialize anything across a boundary at the moment).
I think it probably would be worthwhile to at least have the current behavior explicitly called out somewhere in the documentation (perhaps as a note in http://www.avaje.org/ebean/introquery_partialobject.html or http://www.avaje.org/ebean/introquery_join.html).

Thanks,
-D


On Thursday, 3 May 2012 23:21:17 UTC-4, Rob Bygrave wrote:
>> I wanted to make a copy of an Object

It would be good to understand the purpose of the copy. Typically I'd expect people to use serialization to transport the object graph or persist it to a file system.

adela

unread,
Feb 13, 2013, 6:49:30 PM2/13/13
to eb...@googlegroups.com
Hi Rob,

Can you please provide the entity "Models" code for the examples ebean JSON support?  I followed the examples above and I have been getting many errors:

Here is the error I am getting now:
 Test models.ModelsTest.createDriverRegionRelation failed: No ScalarType registered for class models.Region
[error]     at com.avaje.ebeaninternal.server.persist.Binder.bindObject(Binder.java:164)
[error]     at com.avaje.ebeaninternal.server.query.CQueryPredicates.bind(CQueryPredicates.java:152)
[error]     at com.avaje.ebeaninternal.server.query.CQuery.prepareBindExecuteQuery(CQuery.java:379)
[error]     at com.avaje.ebeaninternal.server.query.CQueryEngine.findMany(CQueryEngine.java:174)
[error]     at com.avaje.ebeaninternal.server.query.DefaultOrmQueryEngine.findMany(DefaultOrmQueryEngine.java:77)
[error]     at com.avaje.ebeaninternal.server.core.OrmQueryRequest.findMap(OrmQueryRequest.java:299)
[error]     at com.avaje.ebeaninternal.server.core.DefaultServer.findMap(DefaultServer.java:1317)
[error]     at com.avaje.ebeaninternal.server.querydefn.DefaultOrmQuery.findMap(DefaultOrmQuery.java:912)
[error]     at com.avaje.ebeaninternal.server.querydefn.DefaultOrmQuery.findMap(DefaultOrmQuery.java:918)
[error]     at com.avaje.ebeaninternal.util.DefaultExpressionList.findMap(DefaultExpressionList.java:189)
[error]     at models.Region.getRegionJson(Region.java:184)
[error]     at models.ModelsTest.createDriverRegionRelation(ModelsTest.java:301)

Here is the Code for the above:

Inside Region.java Entity:

@Entity

@Table(name = "region")

public class Region extends BaseModel {

private static final long serialVersionUID = 1569807861428929099L;


public static Finder<Long, Region> find = new Finder<Long, Region>(

Long.class, Region.class);


@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

@Basic(optional = false)

@Column(name = "id", nullable = false)

private Long id;


@Basic(optional = false)

@Column(name = "name", nullable = false, length = 255)

private String name;


@OneToMany(mappedBy = "region")

private List<DriverRegion> regions;

public static String getRegionJson(Region region) {


Map<String, Region> map = Ebean.find(Region.class)

.where()

.eq("region", region)

.findMap("id", String.class);


JsonContext jsonContext = Ebean.createJsonContext();


JsonWriteOptions jsonWriteOptions = JsonWriteOptions

.parsePath("(name)");


String ret = jsonContext.toJsonString(map, true, jsonWriteOptions);


return ret;

}


Inside DriverRegion.java:

@Entity

@Table(name = "driver_region", uniqueConstraints = @UniqueConstraint(columnNames = {

"driver_id", "region_id" }))

public class DriverRegion extends Model {

private static final long serialVersionUID = 3233477537300849362L;

public static Finder<Long, DriverRegion> find = new Finder<Long, DriverRegion>(

Long.class, DriverRegion.class);

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

@Basic(optional = false)

@Column(name = "id", nullable = false)

private Long id;


@ManyToOne

@JoinColumn(name = "driver_id")

private Driver driver;


@ManyToOne

@JoinColumn(name = "region_id")

private Region region;


@Column(name = "is_active")

private boolean isActive;


...

}

Inside Driver.java:

@Entity

@Table(name = "driver", uniqueConstraints = @UniqueConstraint(columnNames = {

"username" }))

@XmlRootElement

public class Driver extends BaseModel {

private static final long serialVersionUID = -2378727866829160641L;

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

@Column(name = "id", nullable = false)

private Long id;

@OneToMany(mappedBy = "driver")

private List<DriverRegion> regions;

...

}



        {"id":4,"firstName":"Jack","lastName":"Black","phone":null,"mobile":null,"email":"blac...@test.com",

            "group":null,"cretime":"2012-05-04T12:03:10.714Z","updtime":"2012-05-04T12:03:10.714Z"}
    ]},
"3":
{"id":3,"status":"ACTIVE","name":"Fiona",
    "shippingAddress":{"id":4,"line1":"12 Apple St","city":"Auckland"},
    "billingAddress":{"id":3,"line1":"West Coast Rd","line2":"St Lukes","city":"Auckland","cretime":null,
        "country":{"code":"NZ"},"updtime":"2012-05-04T12:03:10.714Z"},
    "contacts":[
        {"id":5,"firstName":"Jim1","lastName":"Cricket","phone":null,"mobile":null,"email":null,
            "group":null,"cretime":"2012-05-04T12:03:10.715Z","updtime":"2012-05-04T12:03:10.715Z"},
        {"id":6,"firstName":"Fred1","lastName":"Blue","phone":null,"mobile":null,"email":null,
            "group":null,"cretime":"2012-05-04T12:03:10.716Z","updtime":"2012-05-04T12:03:10.716Z"},
        {"id":7,"firstName":"Bugs1","lastName":"Bunny","phone":null,"mobile":null,"email":null,
            "group":null,"cretime":"2012-05-04T12:03:10.716Z","updtime":"2012-05-04T12:03:10.716Z"},
        {"id":8,"firstName":"Fiona","lastName":"Black","phone":null,"mobile":null,"email":"blac...@test.com",

Rob Bygrave

unread,
Feb 14, 2013, 3:11:21 AM2/14/13
to eb...@googlegroups.com
Do you mean:

https://github.com/rbygrave/avaje-ebeanorm-server/tree/master/src/test/java/com/avaje/tests/text/json

... and

https://github.com/rbygrave/avaje-ebeanorm-server/tree/master/src/test/java/com/avaje/tests/model/basic ?


--
 
---
You received this message because you are subscribed to the Google Groups "Ebean ORM" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ebean+un...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

adela

unread,
Feb 14, 2013, 1:41:24 PM2/14/13
to eb...@googlegroups.com
Thanks for your reply Rob.  However, after making sure that my entities are similar to the ones in the test source  I still get the same error.
I would appreciate if you can tell me why I am getting this error:

est models.ModelsTest.createDriverRegionRelation failed: No ScalarType registered for class models.Region
[error]     at com.avaje.ebeaninternal.server.persist.Binder.bindObject(Binder.java:164)
[error]     at com.avaje.ebeaninternal.server.query.CQueryPredicates.bind(CQueryPredicates.java:152)
[error]     at com.avaje.ebeaninternal.server.query.CQuery.prepareBindExecuteQuery(CQuery.java:379)
[error]     at com.avaje.ebeaninternal.server.query.CQueryEngine.findMany(CQueryEngine.java:174)
[error]     at com.avaje.ebeaninternal.server.query.DefaultOrmQueryEngine.findMany(DefaultOrmQueryEngine.java:77)
[error]     at com.avaje.ebeaninternal.server.core.OrmQueryRequest.findMap(OrmQueryRequest.java:299)
[error]     at com.avaje.ebeaninternal.server.core.DefaultServer.findMap(DefaultServer.java:1317)
[error]     at com.avaje.ebeaninternal.server.querydefn.DefaultOrmQuery.findMap(DefaultOrmQuery.java:912)
[error]     at com.avaje.ebeaninternal.server.querydefn.DefaultOrmQuery.findMap(DefaultOrmQuery.java:918)
[error]     at com.avaje.ebeaninternal.util.DefaultExpressionList.findMap(DefaultExpressionList.java:189)
[error]     at models.Region.getRegionJson(Region.java:181)
[error]     at models.ModelsTest.createDriverRegionRelation(ModelsTest.java:349)


Here is the code for the above Exception:

public static String getRegionJson(Region region) {


Map<String, Region> map = Ebean.find(Region.class)

.select("name")

.fetch("regions")

.where()

.eq("region", region)

.findMap("id", String.class);


JsonContext jsonContext = Ebean.createJsonContext();


JsonWriteOptions jsonWriteOptions = JsonWriteOptions

.parsePath("(uuid,name)");


String ret = jsonContext.toJsonString(map, true, jsonWriteOptions);


return ret;

}

        {"id":4,"firstName":"Jack","lastName":"Black","phone":null,"mobile":null,"email":"black1@test.com",

            "group":null,"cretime":"2012-05-04T12:03:10.714Z","updtime":"2012-05-04T12:03:10.714Z"}
    ]},
"3":
{"id":3,"status":"ACTIVE","name":"Fiona",
    "shippingAddress":{"id":4,"line1":"12 Apple St","city":"Auckland"},
    "billingAddress":{"id":3,"line1":"West Coast Rd","line2":"St Lukes","city":"Auckland","cretime":null,
        "country":{"code":"NZ"},"updtime":"2012-05-04T12:03:10.714Z"},
    "contacts":[
        {"id":5,"firstName":"Jim1","lastName":"Cricket","phone":null,"mobile":null,"email":null,
            "group":null,"cretime":"2012-05-04T12:03:10.715Z","updtime":"2012-05-04T12:03:10.715Z"},
        {"id":6,"firstName":"Fred1","lastName":"Blue","phone":null,"mobile":null,"email":null,
            "group":null,"cretime":"2012-05-04T12:03:10.716Z","updtime":"2012-05-04T12:03:10.716Z"},
        {"id":7,"firstName":"Bugs1","lastName":"Bunny","phone":null,"mobile":null,"email":null,
            "group":null,"cretime":"2012-05-04T12:03:10.716Z","updtime":"2012-05-04T12:03:10.716Z"},
        {"id":8,"firstName":"Fiona","lastName":"Black","phone":null,"mobile":null,"email":"black2@test.com",

Rob Bygrave

unread,
Feb 14, 2013, 3:19:18 PM2/14/13
to eb...@googlegroups.com
The problem is with:  .eq("region", region)

You can't pass in the bean type into the where .eq() clause.

adela

unread,
Feb 14, 2013, 3:36:00 PM2/14/13
to eb...@googlegroups.com
I fixed it with your suggestion...and it work now.   Thank you very much Rob!
Reply all
Reply to author
Forward
0 new messages