You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@openjpa.apache.org by twelveeighty <tw...@gmail.com> on 2011/06/20 04:17:54 UTC

merge() doesn't refresh many-to-one members unless attribute is called explicitly?

Since this sounds like a basic operation that fails for me, I'm inclined to
think I'm doing something wrong, but I can't find anything similar in the
FAQ or online, so please tell me what I am missing here.

If I use merge() to insert a new entity that has a many-to-one relationship
to another entity, it gets created OK, but the *returned* object from
merge() has the related record defined with ONLY its primary key filled in,
all other attributes on the related record are null. To make it even
weirder: if I call "get<RelatedObject>()" while the object is still
attached, everything is fine.

Here are the details:

OpenJPA version 2.1.1

@Entity
RelatedObject {
  @Id
  private int relatedObjectId;

  @Column(nullable=false, length=255)
  private String shouldNeverBeNull;
}

@Entity
ToBeMergedObject {
  @Id
  private int toBeMergedObjectId;

// @ManyToOne(optional=false, cascade=CascadeType.REFRESH) - tried both,
didn't make a difference
  @ManyToOne(optional=false)
  @JoinColumn(name="relatedObjectId", nullable=false, updatable=false)
  private RelatedObject relatedObject;
}

In the database, there is an existing row for RelatedObject with values (1,
'Some String'). An corresponding instance has been preloaded and has been
detached at some point, let's call this detached instance:
preloadedRelatedObject.

Now, create a new ToBeMergedObject (still outside an EntityManager's
context)

ToBeMergedObject toBeMerged = new ToBeMergedObject();

toBeMerged.setRelatedObject(preloadedRelatedObject);

CASE 1: Create a context and merge the new entity:

EntityManager em = [...]

em.getTransaction().begin();
ToBeMergedObject afterMerge = em.merge(toBeMerged);
em.getTransaction().commit();
em.close();
if (afterMerge.getRelatedObject().getShouldNeverBeNull()==null) {
  log.error("HOW IS THIS POSSIBLE?");
}

CASE 2: Create a context and merge the new entity, but make a (useless) call
to getRelatedObject() before the em.close();

EntityManager em = [...]

em.getTransaction().begin();
ToBeMergedObject afterMerge = em.merge(toBeMerged);
em.getTransaction().commit();
logger.debug("Some useless call: " +
afterMerge.getRelatedObject().getShouldNeverBeNull());
em.close();
if (afterMerge.getRelatedObject().getShouldNeverBeNull()==null) {
  log("doesn't happen");
} else {
  log("NOW IT IS AS EXPECTED - getShouldNeverBeNull() returns a value");
}

As I mention in the definition of the many-to-one pseudo code, I have tried
with cascade=CascadeType.REFRESH but that makes no difference.

Why is it that with a call to the getShouldNeverBeNull() INSIDE the "scope"
of the EntitiyManager that the object gets returned "fully loaded", but
without it, it doesn't?

I appreciate any help you folks can give me on this one. 

--
View this message in context: http://openjpa.208410.n2.nabble.com/merge-doesn-t-refresh-many-to-one-members-unless-attribute-is-called-explicitly-tp6494445p6494445.html
Sent from the OpenJPA Users mailing list archive at Nabble.com.

Re: merge() doesn't refresh many-to-one members unless attribute is called explicitly?

Posted by Michael Dick <mi...@gmail.com>.
Off the top of my head I'd say that the first case will perform slightly
better.  In the first case OpenJPA will get a single connection to the
database, in the second case we'll get two (one for merge, and another for
refresh). You could change this behavior with the
openjpa.ConnectionRetainMode property, but that might have other
implications depending on your application.

Regards,
-mike

--
View this message in context: http://openjpa.208410.n2.nabble.com/merge-doesn-t-refresh-many-to-one-members-unless-attribute-is-called-explicitly-tp6494445p6500031.html
Sent from the OpenJPA Users mailing list archive at Nabble.com.

Re: merge() doesn't refresh many-to-one members unless attribute is called explicitly?

Posted by twelveeighty <tw...@gmail.com>.
Thanks for clarifying this. Yes, when I use CascadeType.MERGE it returns a
"proper" ToBeMerged object.

However, your suggestion made be reconsider my design. Since I really only
need to make sure the returned (and eventually detached) toBeMerged instance
is a completely valid object, I could probably just call
em.refresh(toBeMerged) to generate a "proper" object. Which of the following
two would be more "expensive" in terms of database access, assuming that the
RelatedObject is guaranteed to be always the same? 

CASE 1:

@ManyToOne(optional=false, cascade=CascadeType.MERGE)
@JoinColumn(name="relatedObjectId", nullable=false, updatable=false)
private RelatedObject relatedObject;

and then:

EntityManager em = [...]
em.getTransaction().begin();
ToBeMergedObject afterMerge = em.merge(toBeMerged);
em.getTransaction().commit();
em.close();
return afterMerge;

CASE 2:

@ManyToOne(optional=false, cascade=CascadeType.REFRESH)
@JoinColumn(name="relatedObjectId", nullable=false, updatable=false)
private RelatedObject relatedObject;

and then:

EntityManager em = [...]
em.getTransaction().begin();
ToBeMergedObject afterMerge = em.merge(toBeMerged);
em.getTransaction().commit();
em.refresh(afterMerge);
em.close();
return afterMerge;




Michael Dick wrote:
> 
> Thanks for the thorough description of the problem!
> 
> The problem here is that you're merging a new instance of ToBeMerged,
> which
> has a relationship to a detached RelatedObject.  The merge operation isn't
> cascaded by default, so the rules become a little muddy.
> 
> Adding CascadeType.MERGE will also solve the problem, is that an
> acceptable
> solution for you?
> 
> -mike
> 
> On Sun, Jun 19, 2011 at 9:17 PM, twelveeighty
> &lt;twelve.eighty@gmail.com&gt;wrote:
> 
>> Since this sounds like a basic operation that fails for me, I'm inclined
>> to
>> think I'm doing something wrong, but I can't find anything similar in the
>> FAQ or online, so please tell me what I am missing here.
>>
>> If I use merge() to insert a new entity that has a many-to-one
>> relationship
>> to another entity, it gets created OK, but the *returned* object from
>> merge() has the related record defined with ONLY its primary key filled
>> in,
>> all other attributes on the related record are null. To make it even
>> weirder: if I call "get<RelatedObject>()" while the object is still
>> attached, everything is fine.
>>
>> Here are the details:
>>
>> OpenJPA version 2.1.1
>>
>> @Entity
>> RelatedObject {
>>  @Id
>>  private int relatedObjectId;
>>
>>  @Column(nullable=false, length=255)
>>  private String shouldNeverBeNull;
>> }
>>
>> @Entity
>> ToBeMergedObject {
>>  @Id
>>  private int toBeMergedObjectId;
>>
>> // @ManyToOne(optional=false, cascade=CascadeType.REFRESH) - tried both,
>> didn't make a difference
>>  @ManyToOne(optional=false)
>>  @JoinColumn(name="relatedObjectId", nullable=false, updatable=false)
>>  private RelatedObject relatedObject;
>> }
>>
>> In the database, there is an existing row for RelatedObject with values
>> (1,
>> 'Some String'). An corresponding instance has been preloaded and has been
>> detached at some point, let's call this detached instance:
>> preloadedRelatedObject.
>>
>> Now, create a new ToBeMergedObject (still outside an EntityManager's
>> context)
>>
>> ToBeMergedObject toBeMerged = new ToBeMergedObject();
>>
>> toBeMerged.setRelatedObject(preloadedRelatedObject);
>>
>> CASE 1: Create a context and merge the new entity:
>>
>> EntityManager em = [...]
>>
>> em.getTransaction().begin();
>> ToBeMergedObject afterMerge = em.merge(toBeMerged);
>> em.getTransaction().commit();
>> em.close();
>> if (afterMerge.getRelatedObject().getShouldNeverBeNull()==null) {
>>  log.error("HOW IS THIS POSSIBLE?");
>> }
>>
>> CASE 2: Create a context and merge the new entity, but make a (useless)
>> call
>> to getRelatedObject() before the em.close();
>>
>> EntityManager em = [...]
>>
>> em.getTransaction().begin();
>> ToBeMergedObject afterMerge = em.merge(toBeMerged);
>> em.getTransaction().commit();
>> logger.debug("Some useless call: " +
>> afterMerge.getRelatedObject().getShouldNeverBeNull());
>> em.close();
>> if (afterMerge.getRelatedObject().getShouldNeverBeNull()==null) {
>>  log("doesn't happen");
>> } else {
>>  log("NOW IT IS AS EXPECTED - getShouldNeverBeNull() returns a value");
>> }
>>
>> As I mention in the definition of the many-to-one pseudo code, I have
>> tried
>> with cascade=CascadeType.REFRESH but that makes no difference.
>>
>> Why is it that with a call to the getShouldNeverBeNull() INSIDE the
>> "scope"
>> of the EntitiyManager that the object gets returned "fully loaded", but
>> without it, it doesn't?
>>
>> I appreciate any help you folks can give me on this one.
>>
>> --
>> View this message in context:
>> http://openjpa.208410.n2.nabble.com/merge-doesn-t-refresh-many-to-one-members-unless-attribute-is-called-explicitly-tp6494445p6494445.html
>> Sent from the OpenJPA Users mailing list archive at Nabble.com.
>>
> 


--
View this message in context: http://openjpa.208410.n2.nabble.com/merge-doesn-t-refresh-many-to-one-members-unless-attribute-is-called-explicitly-tp6494445p6498536.html
Sent from the OpenJPA Users mailing list archive at Nabble.com.

Re: merge() doesn't refresh many-to-one members unless attribute is called explicitly?

Posted by Michael Dick <mi...@gmail.com>.
Thanks for the thorough description of the problem!

The problem here is that you're merging a new instance of ToBeMerged, which
has a relationship to a detached RelatedObject.  The merge operation isn't
cascaded by default, so the rules become a little muddy.

Adding CascadeType.MERGE will also solve the problem, is that an acceptable
solution for you?

-mike

On Sun, Jun 19, 2011 at 9:17 PM, twelveeighty <tw...@gmail.com>wrote:

> Since this sounds like a basic operation that fails for me, I'm inclined to
> think I'm doing something wrong, but I can't find anything similar in the
> FAQ or online, so please tell me what I am missing here.
>
> If I use merge() to insert a new entity that has a many-to-one relationship
> to another entity, it gets created OK, but the *returned* object from
> merge() has the related record defined with ONLY its primary key filled in,
> all other attributes on the related record are null. To make it even
> weirder: if I call "get<RelatedObject>()" while the object is still
> attached, everything is fine.
>
> Here are the details:
>
> OpenJPA version 2.1.1
>
> @Entity
> RelatedObject {
>  @Id
>  private int relatedObjectId;
>
>  @Column(nullable=false, length=255)
>  private String shouldNeverBeNull;
> }
>
> @Entity
> ToBeMergedObject {
>  @Id
>  private int toBeMergedObjectId;
>
> // @ManyToOne(optional=false, cascade=CascadeType.REFRESH) - tried both,
> didn't make a difference
>  @ManyToOne(optional=false)
>  @JoinColumn(name="relatedObjectId", nullable=false, updatable=false)
>  private RelatedObject relatedObject;
> }
>
> In the database, there is an existing row for RelatedObject with values (1,
> 'Some String'). An corresponding instance has been preloaded and has been
> detached at some point, let's call this detached instance:
> preloadedRelatedObject.
>
> Now, create a new ToBeMergedObject (still outside an EntityManager's
> context)
>
> ToBeMergedObject toBeMerged = new ToBeMergedObject();
>
> toBeMerged.setRelatedObject(preloadedRelatedObject);
>
> CASE 1: Create a context and merge the new entity:
>
> EntityManager em = [...]
>
> em.getTransaction().begin();
> ToBeMergedObject afterMerge = em.merge(toBeMerged);
> em.getTransaction().commit();
> em.close();
> if (afterMerge.getRelatedObject().getShouldNeverBeNull()==null) {
>  log.error("HOW IS THIS POSSIBLE?");
> }
>
> CASE 2: Create a context and merge the new entity, but make a (useless)
> call
> to getRelatedObject() before the em.close();
>
> EntityManager em = [...]
>
> em.getTransaction().begin();
> ToBeMergedObject afterMerge = em.merge(toBeMerged);
> em.getTransaction().commit();
> logger.debug("Some useless call: " +
> afterMerge.getRelatedObject().getShouldNeverBeNull());
> em.close();
> if (afterMerge.getRelatedObject().getShouldNeverBeNull()==null) {
>  log("doesn't happen");
> } else {
>  log("NOW IT IS AS EXPECTED - getShouldNeverBeNull() returns a value");
> }
>
> As I mention in the definition of the many-to-one pseudo code, I have tried
> with cascade=CascadeType.REFRESH but that makes no difference.
>
> Why is it that with a call to the getShouldNeverBeNull() INSIDE the "scope"
> of the EntitiyManager that the object gets returned "fully loaded", but
> without it, it doesn't?
>
> I appreciate any help you folks can give me on this one.
>
> --
> View this message in context:
> http://openjpa.208410.n2.nabble.com/merge-doesn-t-refresh-many-to-one-members-unless-attribute-is-called-explicitly-tp6494445p6494445.html
> Sent from the OpenJPA Users mailing list archive at Nabble.com.
>