Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [eclipselink-dev] Question aboutEmbeddedAccessor#processEmbeddableClass()


Hi Mark.
 
So I didn't try this but, here's what I'm thinking off the cuff (note: I didn't compile or validate this code)
 
From the 'owning' entity accessor, when processing the accessors check for embedded accessors and call a processPrefix method it:
 
if (accessor.isEmbedded()) {
    accessor.processPrefix(accessor.getPrefix(), getProject().getEmbeddableAccessor(accessor.getReferenceClass()), accessor.getDescriptor().getMappingForAttributeName(accessor.getAttributeName));
}
 
where the process prefix method looks something like:
 
protected void processPrefix(String prefix, EmbeddableAccessor accessor, AggregateObjectMapping mapping)  {
  for (MappingAccessor accessor : accessor.getDescriptor().getAccessors()) {
    if (accessor.isEmbedded()) {
      ((EmbeddedAccessor) accessor).processPrefix(prefix + accessor.getPrefix(), getProject().getEmbeddableAccessor(accessor.getReferenceClass()), mapping);
    } else {
      if (prefix == null) {
        // no prefix to apply ...
        break;
      } else { 
        String attributeName = accessor.getAttributeName();

        DatabaseMapping aggregateMapping = getReferenceDescriptor().getMappingForAttributeName(attributeName);
             
        if (aggregateMapping.getField() != null) {
          String sourceFieldName = aggregateMapping.getField().getName(); 
          String prefixedFieldName = prefix + sourceFieldName;
          mapping.addFieldNameTranslation(prefixedFieldName, sourceFieldName);
        }
      }
    }
  }
}
 
Anyway, I'm thinking something along that line, likely still some issues to work out, but give it a try and see what you get.
 
Cheers,
Guy
 
 
----- Original Message -----
From: "Mark Struberg" <struberg@xxxxxxxx>
To: <eclipselink-dev@xxxxxxxxxxx>; "Guy Pelletier" <guy.pelletier@xxxxxxxxxx>
Sent: Monday, November 17, 2008 11:20 AM
Subject: Re: [eclipselink-dev] Question aboutEmbeddedAccessor#processEmbeddableClass()

Hi Guy!

Txs 4 the help. I tried to implement the functionality as you said, but while it works like a charm if I only have 1 layer deep embeddings, I get problems with nested ones.

Maybe you can take a look at the attached patch (I wrote a few invoker IT's for this situation, which I will published later on after I did some cleanup)?

The problematic situation:

@Embeddable
public class Price {
@Column(precision=12, scale=2)
private BigDecimal amount;
private String     currency;
}

@Embeddable
public class TaxedPrice {
@Embedded(prefix = "N_") private Price netPrice;
@Embedded(prefix = "G_") private Price grossPrice;
}

@Entity
public class PriceWatch {
@Id @GeneratedValue private int id;
@Embedded(prefix = "H_") private TaxedPrice highestPrice;
@Embedded(prefix = "L_") private TaxedPrice lowestPrice;
}


So for the table PriceWatch I'd like to get the following columns:
ID
H_N_AMOUNT
H_N_CURRENCY
H_G_AMOUNT
H_G_CURRENCY
L_N_AMOUNT
L_N_CURRENCY
L_G_AMOUNT
L_G_CURRENCY

Since the innermost classes don't have the information of the prefixes L_ and H_, this info has to be 'pushed' into the objects recursively afterwards, which is utterly broken the way I tried it. I found no way to handle me down the embeddings to the fields e.g. via:

                if (aggregateMapping instanceof AggregateObjectMapping) {
                AggregateObjectMapping aggregateObjMapping = (AggregateObjectMapping) aggregateMapping;
                Map<String, String> m = aggregateObjMapping.getAggregateToSourceFieldNames();
               
                // add the m_prefix to all the mappings
                for(Map.Entry<String, String> fieldMapping : m.entrySet()) {
                fieldMapping.setValue(m_prefix + fieldMapping.getValue());
                }
                }

it seems that I always get the same attributeMapping for all embeddings which leads to:
INSERT INTO PRICEWATCH (ID, L_H_N_AMOUNT, L_H_N_CURRENCY, L_H_G_AMOUNT, L_H_G_CURRENCY, L_H_N_AMOUNT, L_H_N_CURRENCY, L_H_G_AMOUNT, L_H_G_CURRENCY)...

An alternative handling would be to pass the prefix up the chain, so all prefixes would stack up almost automatic.
But this would require the fix for each embedding has it's own EmbeddableAccessor first.

EmbeddedAccessor.java line # 393 something like:

            accessor.setOwningDescriptor(getOwningDescriptor());
+           accessor.setPrefix(m_prefix);
            accessor.process();
            accessor.setIsProcessed();   

and m_prefix stacking up himself like
+           m_prefix += (embedded == null) ? "" : (String) MetadataHelper.invokeMethod("prefix", embedded);


But I think there must be somethings much more simple than that. It's getting to complicated. An old lore says: there is no problem which can't be solved by adding an additional step of indirection, but I'm not a friend of these 'bypass' operations.

Maybe you have some tip for me again?

txs and LieGrue,
strub



--- Guy Pelletier <
guy.pelletier@xxxxxxxxxx> schrieb am Mi, 12.11.2008:

> Von: Guy Pelletier <
guy.pelletier@xxxxxxxxxx>
> Betreff: Re: [eclipselink-dev] Question aboutEmbeddedAccessor#processEmbeddableClass()
> An: "Mark Struberg" <
struberg@xxxxxxxx>, eclipselink-dev@xxxxxxxxxxx
> Datum: Mittwoch, 12. November 2008, 20:35
> Hi Mark,
>
> So I wouldn't use the word store, but rather a new
> EmbeddableAccessor should be created and processed for each
> usage of an Embedded. Internally (beyond the metadata
> processing objects) EclipseLink uses/has only one class
> descriptor to every class (entity or embeddable) of the
> persistence unit.
>
> I took a  look at your prefix annotation. I would think you
> could achieve this functionality by adding another
> processing method to EmbeddedAccessor to iterate over the
> mappings of the embeddable class (once it has been
> processed) and add a field name translation (just like we do
> for attribute overrides) using the prefix to each field of
> the mappings of the embeddable? If there is a nested case,
> you would bury down into its mappings and apply the prefix,
> unless the nested embedded defined its own prefix, and so
> on.
>
> So I'm not sure if in your case you need to worry about
> the notion of the owning descriptor? The notion of the
> owning descriptor was introduced post JPA 1.0 to facilitate
> embeddables (nested or not) to specify metadata beyond what
> was allowed in the spec. Namely things like Id's,
> GeneratedValue's etc .. those items which required
> metadata settings on to the owning entity. Unfortunately in
> the current design those metadata items are only processed
> for the first entity that uses the shared embedded (this is
> a bug, which I pointed out earlier).
>
> The model you outlined below should work no problem (it
> doesn't have owning descriptor dependencies)
>
> I hope this helps!
>
> Cheers,
> Guy
>
> ----- Original Message ----- From: "Mark
> Struberg" <
struberg@xxxxxxxx>
> To: <
eclipselink-dev@xxxxxxxxxxx>; "Guy
> Pelletier" <
guy.pelletier@xxxxxxxxxx>
> Sent: Wednesday, November 12, 2008 12:29 PM
> Subject: Re: [eclipselink-dev] Question
> aboutEmbeddedAccessor#processEmbeddableClass()
>
>
> Hi Guy!
>
> Txs 4 the quick response!
>
> So (I better repeat this to make sure I understood it
> correctly) the question is: should we store the aggregated
> EmbeddableAccessor once for each table, or once for every
> usage of @Embedded, correct?
>
> As you've maybe read, I'm currently working on a
> prefix annotation for embedded classes. If this will be part
> of @Embedded or an own annotation doesn't imho change
> much, in any case we have add the m_prefix handling to the
> EmbeddedAccessor. So, I'm sure there is another way in

> the chain to add the prefix to the column names, but my gut
> feeling tells me that this would be much easier to implement
> if there is an EmbeddableAccessor for every Embedding.
> Including nested embeddings.
>
> Btw, what about nested embeddings currently?
> I did not debug through, but can you imagine a situation
> where we do also need the info parted in this case?
>
> An example:
>
> @Embeddable
> public class Price {
> private BigDecimal amount;
> private String     currency;
> }
>
> @Embeddable
> public class ListedPrice {
> private int someValue;
>
> @Embedded
> @SomeFancyPriceModification
> private Price lPrice;
> }
>
> @Entity
> public class Purchase {
> @Id @GeneratedValue private int id;
>
> @Embedded
> private ListedPrice listedPrice;
>
> @Embedded
> @AnotherFancyPriceModification
> private Price soldPrice;
> }
>
> 1.) Are there any valid annotations which may affect the
> fields? I know about the AttributeOverrides which has a
> special handling in EmbeddedAccessor, so this should not be
> a problem for @Embedding. But what happens if
> AttributeOverrides is being used inside a nested class
> (ListedPrice)?
> 2.) If so, may this be handled if we only have one
> EmbeddableAccessor per OwningDescriptor?
>
> Humm, my brain is smoking, hope this post isn't getting
> too confusing ;)
>
>
> LieGrue,
> strub
>
> P.S.:
> And a few additional thoughts coming to my mind:
> a) What would be the downside of storing the
> EmbeddableAccesor for each @Embedded usage? We have to store
> one for each entity anyway. Technically his would imho only
> have an effect if there are many embeddings of the same type
> in the same table. Logically it is simply a difference if an
> Accessor represents a 'database archetype' or a
> specific usage of this archetype.
> b) Could we split the EmbeddedAccessor in a
> 'static' part and a part for each embedding?
> c) What about adding back-references to the actual
> EmbeddedAccessor for each processing step and stay with 1
> EmbeddableAccessor (which then has to lookup varying parts
> from it's embedding)?
>
>
> --- Guy Pelletier <
guy.pelletier@xxxxxxxxxx> schrieb
> am Mi, 12.11.2008:
>
> > Von: Guy Pelletier <
guy.pelletier@xxxxxxxxxx>
> > Betreff: Re: [eclipselink-dev] Question
> aboutEmbeddedAccessor#processEmbeddableClass()
> > An:
eclipselink-dev@xxxxxxxxxxx
> > Datum: Mittwoch, 12. November 2008, 16:05
> > Hi Mark,
> >
> > Happy to hear you're enjoying working with the
> code.
> >
> > What you have pointed out is indeed a problem and is
> > problamatic for a number of use cases. See the
> following bug
> > (
https://bugs.eclipse.org/bugs/show_bug.cgi?id=250144)
> >
> > The notion of the owning descriptor should remain
> however
> > (e.g. an id field defined on an embeddable requires
> setting
> > metadata on the actual owning entity) , but the
> problem as
> > you have pointed out is that the embeddable class is
> only
> > processed once from the first entity that references
> it. So
> > this is incorrect. The embeddable class should likely
> be
> > cloned (similarly as we do with mapped superclasses)
> and
> > re-processed using the context or each owning
> descriptor.
> > Note, the whole class likely will not need to be
> > re-processed, likely only the accessors and again
> you'll
> > have to be careful as to which accessors gets
> re-processed
> > since you don't want all the metadata from your
> > embeddable class stored on the owning descriptor.
> >
> > See the reloadMappedSuperclass method from ORMetadata.
> > Notice, it clones a mapped superclass by reloading it
> from
> > XML. In your case you would have to handle the case
> where
> > the Embeddable was not loaded from XML (the accessible
> > object will not have an associated entity mappings
> object)
> > and you'd likely just have to create a new
> instance of
> > the EmbeddableAccessor. I've just recently built a
> > similar method for Entities (in my current work for
> > TABLE_PER_CLASS support, see below)
> >
> > /**
> >  * INTERNAL:
> >  * This method should be called to reload an entity
> (that
> > was either
> >  * loaded from XML or an annotation) as a way of
> cloning
> > it. This is needed
> >  * when we process TABLE_PER_CLASS inheritance. We
> must
> > process the parent
> >  * classes for every subclasses descriptor. The
> processing
> > is similar to
> >  * that of processing a mapped superclass, in that we
> > process the parents
> >  * with the subclasses context (that is, the
> descriptor we
> > are given).
> > */
> > protected EntityAccessor reloadEntity(EntityAccessor
> > entity, MetadataDescriptor descriptor) {
> >     if (m_accessibleObject.getEntityMappings() ==
> null) {
> >         // Create a new EntityAccesor.
> >         EntityAccessor entityAccessor = new
> > EntityAccessor(entity.getAnnotation(),
> > entity.getJavaClass(), entity.getProject());
> >         // Things we care about ...
> >         entityAccessor.setDescriptor(descriptor);
> >
> >
> entityAccessor.getDescriptor().setDefaultAccess(entity.getDescriptor().getDefaultAccess());
> >         return entityAccessor;
> >     } else {
> >         return
> >
> m_accessibleObject.getEntityMappings().reloadEntity(entity,
> > descriptor);
> >     }
> > }
> >
> > Hope this helps!
> >
> > Cheers,
> > Guy
> >
> >
> > ----- Original Message ----- From: "Mark
> Struberg" <
struberg@xxxxxxxx>
> > To: <
eclipselink-dev@xxxxxxxxxxx>
> > Sent: Wednesday, November 12, 2008 6:04 AM
> > Subject: [eclipselink-dev] Question
> > aboutEmbeddedAccessor#processEmbeddableClass()
> >
> >
> > Hi!
> >
> > I've read through the eclipselink sources and like
> to
> > add a few features.
> >
> > Generally, I'd to say that it is a really well
> done and
> > _very_ readable source! I did look at the sources for
> only 2
> > evenings now (and I really enjoyed digging into it),
> so
> > don't be rude if I completely misinterpreted/not
> > understood something yet :)
> >
> > There's a point in the EmbeddedAccesor which I do
> not
> > understand:
> >
> > In the function processEmbeddableClass(), a
> > 'cached' EmbeddedAccessor instance is
> retrieved
> > (line # 299)
> >         EmbeddableAccessor accessor =
> >
> getProject().getEmbeddableAccessor(getReferenceClassName());
> >
> > So I'll get the same EmbeddableAccessor instance
> for
> > EVERY embedding of the same class.
> >
> > A bit later in the source (line # 349), this
> > EmbeddableAccessor will be filled with the
> OwningDescriptor:
> >
> > accessor.setOwningDescriptor(getOwningDescriptor());
> >
> > BUT: imho the owning descriptor may be different for
> each
> > @Embedded !?
> >
> > In praxis this means, that the EmbeddableAccessor
> always
> > points to the Table-Descriptor of the 'first'
> > occurence (which one this ever may be -> random
> > generator?), and the other ones may simply contain
> wrong
> > values.
> >
> > My example:
> >
> > @Embeddable
> > public class Price {
> > private BigDecimal amount;
> > private String     currency;
> > }
> >
> > @Entity
> > public class Purchase {
> > @Id
> > @GeneratedValue
> > private int id;
> >
> > @Embedded(prefix = "N_")
> > private Price netPrice;
> >
> > @Embedded(prefix = "G_")
> > private Price grossPrice;
> > }
> >
> > @Entity
> > public class OtherUseOfPrice {
> > @Id
> > @GeneratedValue
> > private int id;
> >
> > @Embedded(prefix = "O_")
> > private Price otherPrice;
> > }
> >
> > In the debugger I got the following memory addresses:
> >
> > O_ id=40 accessor=98 getowningDescriptor()= 45
> > N_ id=101 accessor=98 getowningDescriptor()= 102
> > G_ id=116 accessor=98 getowningDescriptor()= 102
> >
> > And as expected, the accessor of the embeddings of
> netPrice
> > (N_) and grossPrice (G_) point to the wrong
> > m_owningDescriptor 45 instead of the correct 102
> >
> > Does this have any impact on the result? If not, we
> also
> > could remove the owningDescriptor from the
> > EmbeddableAccessor, otherwise it may be a source of
> > problems, isn't?
> >
> >
> >
> >
> > _______________________________________________
> > eclipselink-dev mailing list
> >
eclipselink-dev@xxxxxxxxxxx
> >
>
https://dev.eclipse.org/mailman/listinfo/eclipselink-dev_______________________________________________
> > eclipselink-dev mailing list
> >
eclipselink-dev@xxxxxxxxxxx
> >
>
https://dev.eclipse.org/mailman/listinfo/eclipselink-dev



Back to the top