OneToOne how to fetch join data with a transient field ?

1,131 views
Skip to first unread message

Daniel Guryca

unread,
Oct 4, 2010, 6:58:20 AM10/4/10
to play-fr...@googlegroups.com
Hi,

I HAVE THESE 2 MODELS:

@Entity
public class Prod extends Model {

@OneToOne(fetch = FetchType.LAZY)
public ProdDetail prodDetail;

@Transient
public String descr;

public Prod() {
super();
this.prodDetail = new ProdDetail();
}

public String getDescr() {
ProdDetail detail = this.prodDetail;
return detail.descr;
}
}

@Entity
public class ProdDetail extends Model {

@Required
public String descr;

public ProdDetail() {
super();
}
}


FIRST CASE:
public static void index() {
Prod prod = Prod.find("").first();
ProdDetail detail = prod.prodDetail;
Logger.info(detail.descr);
}


SECOND CASE:
public static void index2() {
Prod prod = Prod.find("").first();
OR THIS QUERY
Prod prod = Prod.find("select p FROM Prod p JOIN p.prodDetail
pd").first();

ProdDetail detail = prod.prodDetail;
Logger.info(prod.descr); // RETURNS NULL - WHY ??
}

in 1:
It returns the right content of ProdDetail.

In 2:
ProductDetail is set but descr field is null.


Any help is appreciated.

Thanks
Daniel

Nicolas Leroux

unread,
Oct 4, 2010, 7:16:12 AM10/4/10
to play-fr...@googlegroups.com
Do you have product details description that are nulls? I noticed that you are not ordering your results, that might be the problem.
Try with .order("asc") on both queries for example?

Nicolas

> --
> You received this message because you are subscribed to the Google Groups "play-framework" group.
> To post to this group, send email to play-fr...@googlegroups.com.
> To unsubscribe from this group, send email to play-framewor...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/play-framework?hl=en.
>

Guillaume Bort

unread,
Oct 4, 2010, 7:20:58 AM10/4/10
to play-fr...@googlegroups.com
And

Logger.info(prod.getDescr());

Works?

> --
> You received this message because you are subscribed to the Google Groups "play-framework" group.
> To post to this group, send email to play-fr...@googlegroups.com.
> To unsubscribe from this group, send email to play-framewor...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/play-framework?hl=en.
>
>

--
Guillaume Bort, http://guillaume.bort.fr

For anything work-related, use g...@zenexity.fr; for everything else,
write guillau...@gmail.com

Daniel Guryca

unread,
Oct 4, 2010, 7:36:45 AM10/4/10
to play-fr...@googlegroups.com
No, it does not work.

Daniel

Nicolas Martignole

unread,
Oct 4, 2010, 7:41:11 AM10/4/10
to play-fr...@googlegroups.com
Hi Daniel

Just a bit of curiosity : why do you hide the descr attribute with the
Transient getter ?

Else the answer to your question is that if you declare a public
method name "getDescr"
on your Entity Prod, then it cannot be enriched at runtime by
Hibernate to perform the lazy-loading.
Try to rename-it to getMyDescription and it should work.

A simplier and better approach would be to stick to the Active Record pattern.

On your Prod entity, do not hide the "descr" attribute from Prod with
an error-prone getter.
You could rename attribute descr to description on ProdDetail, then
use directly the statement below
on the groovy side in your view.

My product description = ${prod?.prodDetail?.description}

Nicolas

2010/10/4 Guillaume Bort <guillau...@gmail.com>:

Daniel Guryca

unread,
Oct 4, 2010, 7:52:40 AM10/4/10
to play-fr...@googlegroups.com
I have only 1 record in my DB so this is not any issue.

Daniel

Daniel Guryca

unread,
Oct 4, 2010, 8:19:21 AM10/4/10
to play-fr...@googlegroups.com
Hi,

On Mon, Oct 4, 2010 at 1:41 PM, Nicolas Martignole
<nic...@martignole.net> wrote:
> Hi Daniel
>
> Just a bit of curiosity : why do you hide the descr attribute with the
> Transient getter ?

Because in my real application descr field is a blob which can be even
100kB large. So I want to fetch it lazily via OneToOne.

>
> Else the answer to your question is that if you declare a public
> method name "getDescr"
> on your Entity Prod, then it cannot be enriched at runtime by
> Hibernate to perform the lazy-loading.
> Try to rename-it to getMyDescription and it should work.
>
> A simplier and better approach would be to stick to the Active Record pattern.
>
> On your Prod entity, do not hide the "descr" attribute from Prod with
> an error-prone getter.
> You could rename attribute descr to description on ProdDetail, then
> use directly the statement below
> on the groovy side in your view.
>
> My product description = ${prod?.prodDetail?.description}

Yes I know I can use it this way in groovy but I use these methods in
java since in my real application I have descrDe, descrEn, ... and
descr is just a transient field which calls getDescr() method (because
of nice Play magic).

Guillaume Bort

unread,
Oct 4, 2010, 8:59:58 AM10/4/10
to play-fr...@googlegroups.com
Can you check the type of the prodDetail object in the two cases?

Daniel Guryca

unread,
Oct 4, 2010, 9:16:08 AM10/4/10
to play-fr...@googlegroups.com
They are same in both cases:
class models.ProdDetail_$$_javassist_135

Daniel

damian

unread,
Oct 4, 2010, 5:56:59 PM10/4/10
to play-framework
Your @Transient field "descr" is never populated and Hibernate has no
way of knowing that it is supposed to refer to the "descr" from
Detail. Try adding a @PostLoad'd method to Prod. Eg:

@PostLoad
public void loadDescr() {
this.descr = prodDetail.descr;
}

Or; just refer to getDescr() rather than "descr".

Btw, @OneToOne is not such a great construct for Hibernate as it will
*never* be lazy loaded. You're better to make it @OneToMany and fake
up the single relationship.

Cheers,

Damian.

Daniel Guryca

unread,
Oct 5, 2010, 4:58:26 AM10/5/10
to play-fr...@googlegroups.com
Well, I know that transient descr is never populated but it does not matter.
Even if I completely remove this transient field (since I do not need
it) and check everything only via getDescr() it still does not work.
If I call this.prodDetail in a Prod model I would expect that
prodDetail field will be auto populated since prodDetail is anotated
with @OneToOne.
It's what I do not understand.

Any other idea ?
Thank you

Daniel

Daniel Guryca

unread,
Oct 5, 2010, 9:52:27 AM10/5/10
to play-fr...@googlegroups.com
Guys it seems to be a BUG.

I completely removed transient field descr since it's not the core of a problem.
Problem is a descr field in a ProdDetail model and a getDescr() method
in a Prod model.

If I rename descr field in a ProdDetail model to description
everything starts to work as expected.
What the hell is going on ?


If field name (description located in a ProdDetail model) is same with
name of a method located in a Prod model it stops working !!

I really do not uderstand what's wrong. Method is and should be
totally independant of a field which is located in other model.

index and index2 actions should give same result "test".

Just my complete controller + models:


public class Prods extends Controller {
private static Prod createOrGet() {


Prod prod = Prod.find("").first();

if(prod == null) {
Prod p = new Prod();
p.prodDetail.description = "test";
p.save();

prod = Prod.find("").first();
}

return prod;
}

public static void index() {
Prod prod = createOrGet();

ProdDetail detail = prod.prodDetail;

String descr = detail.description;

renderText(descr);
}


public static void index2() {
Prod prod = createOrGet();

String descr = prod.getDescription();

renderText(descr);
}
}


@Entity
public class Prod extends Model {

@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinColumn(name = "fk_prodDetail")
public ProdDetail prodDetail;



public Prod() {
super();
this.prodDetail = new ProdDetail();
}

public String getDescription() {
ProdDetail detail = this.prodDetail;
return detail.description;
}
}

@Entity
public class ProdDetail extends Model {

@Required
public String description;

public ProdDetail() {
super();
}
}

NOT WORKING VERSION:

public class Prods extends Controller {
private static Prod createOrGet() {


Prod prod = Prod.find("").first();

if(prod == null) {
Prod p = new Prod();
p.prodDetail.description = "test";
p.save();

prod = Prod.find("").first();
}

return prod;
}

public static void index() {
Prod prod = createOrGet();

ProdDetail detail = prod.prodDetail;

String descr = detail.description;

renderText(descr);
}


public static void index2() {
Prod prod = createOrGet();

String descr = prod.getDescription();

renderText(descr);
}
}


@Entity
public class Prod extends Model {

@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinColumn(name = "fk_prodDetail")
public ProdDetail prodDetail;



public Prod() {
super();
this.prodDetail = new ProdDetail();
}

public String getDescription() {
ProdDetail detail = this.prodDetail;
return detail.description;
}
}

@Entity
public class ProdDetail extends Model {

@Required
public String description;

public ProdDetail() {
super();

Daniel Guryca

unread,
Oct 5, 2010, 10:03:57 AM10/5/10
to play-fr...@googlegroups.com
Both version which I sent were not working versions, sorry so now a
working version (with just changed method name so that method is not
same to a field name).

A WORKING VERSION:

public class Prods extends Controller {
private static Prod createOrGet() {
Prod prod = Prod.find("").first();

if(prod == null) {
Prod p = new Prod();
p.prodDetail.description = "test";
p.save();

prod = Prod.find("").first();
}

return prod;
}

public static void index() {
Prod prod = createOrGet();

ProdDetail detail = prod.prodDetail;

String descr = detail.description;

renderText(descr);
}


public static void index2() {
Prod prod = createOrGet();

String descr = prod.getDescriptionCHANGED();

renderText(descr);
}
}


@Entity
public class Prod extends Model {

@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinColumn(name = "fk_prodDetail")
public ProdDetail prodDetail;

public Prod() {
super();
this.prodDetail = new ProdDetail();
}

public String getDescriptionCHANGED() {

Guillaume Bort

unread,
Oct 5, 2010, 11:50:10 AM10/5/10
to play-fr...@googlegroups.com
Ok you are right, it is probably a bug caused by an incompatibility
between the bytecode enhancement of Play and the one done by
hibernate. Can you report a bug with your test case?

--

Daniel Guryca

unread,
Oct 5, 2010, 12:11:15 PM10/5/10
to play-fr...@googlegroups.com

Dunsun

unread,
Nov 12, 2010, 7:46:29 AM11/12/10
to play-framework
Guys any progress on this ticket ?
I have been bitten by this bug again.

Daniel

On 5 říj, 17:11, Daniel Guryca <dun...@gmail.com> wrote:
> Ticket created:
>
> http://play.lighthouseapp.com/projects/57987-play-framework/tickets/2...
>
> Thank you
> Daniel
> >>>> On Mon, Oct 4, 2010 at 11:56 PM, damian <damianhar...@gmail.com> wrote:
> >>>>> Your @Transient field "descr" is never populated and Hibernate has no
> >>>>> way of knowing that it is supposed to refer to the "descr" from
> >>>>> Detail. Try adding a @PostLoad'd method to Prod. Eg:
>
> >>>>> @PostLoad
> >>>>> public void loadDescr() {
> >>>>>   this.descr = prodDetail.descr;
> >>>>> }
>
> >>>>> Or; just refer to getDescr() rather than "descr".
>
> >>>>> Btw, @OneToOneis not such a great construct for Hibernate as it will
> ...
>
> číst dál »
Reply all
Reply to author
Forward
0 new messages