I'm VP of Engineering at Tasktop Technologies, improving the effectiveness of developers worldwide via Tasktop's revolutionary task-focused interface, built on the Eclipse Mylyn open source framework. David has posted 28 posts at DZone. You can read more from them at their website. View Full User Profile

JPA 2.0: Why AccessType is Relevant

02.18.2009
| 19696 views |
  • submit to reddit

It has long been debated whether field access is superior to property access for JPA entities. Most arguments that I've seen hinge on performance versus encapsulation. While these arguments may be interesting from a theoretical perspective, control over field access is in fact very useful in solving real-world problems.

Field access allows code to distinguish between the JPA provider setting properties while loading an entity versus application code setting a property.

This was very useful for a project that I'm involved with that has de-normalized data or other fields that are computed and persisted. Here's how it works:

  • In the setter for various properties that affect the computation of a de-normalized property, the value of the de-normalized property is set to null. @AccessType(FIELD) is used for these properties.
  • In the getter for the de-normalized property, if the value is null the de-normalized value is recomputed.
  • In a @PrePersist method, the getter is called to ensure that the de-normalized value is properly persisted.

 

Using this technique maintaining de-normalized data becomes simple and robust.

While this feature has been available within Hibernate since 3.0, I'm glad to see that JPA 2.0 (JSR 317) introduces a standard annotation to control this behaviour.

From http://greensopinion.blogspot.com

0
Your rating: None
Published at DZone with permission of its author, David Green.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Jeroen Wenting replied on Wed, 2009/02/18 - 8:56am

What would be far more important for JPA is adding the ability to easily map calls to stored procedures through JPA constructs.

Robin Bygrave replied on Wed, 2009/02/18 - 5:04pm

Hi David,

The way I see it you can de-normalise data in the database and/or in the java entity code. Given your last point about @PrePersist it seems the data is denormalised in the database - but I think it would be helpful to make this clear.

Thanks, Rob.

Robin Bygrave replied on Wed, 2009/02/18 - 5:46pm

Field vs Property access (A vendor opinion)

Having written an orm (Ebean ORM) that has Field and Property access I'll just fire out my opinion on this issue of Field vs Property access.

The *ONLY* reason I would use Property access is where I could NOT use Field access.

If you use Property access it is possible to get 'wierd' behaviour if you add logic to your entity beans (aka have more than just getters and setters). Conversely if you use Field access you can do what you like (remove getters or setters, add logic into your entity bean etc) and it will all work as you'd expect (no strange side effects).

For those interested... in the Ebean user guide I go through the differences in byte code Ebean uses between Property and Field access to show why this is the case. Obviously this is Ebean specific enhancement but it would apply generally to the other ORM's.

- Performance: Yes IMO there will likely be a slight gain with Field access (not really significant though imo)

- Encapsulation: Most of the arguments I've seen miss the point about how this works 

- Data type conversion: Some give this as a reason to use Property access but IMO there are better approaches

- webstart: You can't use javaagent (you have to use other enhancement such as Ant etc OR use "Subclass generation" typically with Property access)

For me, the argument against Field acess is that it generally requires enhancement via Javaagent, Ant or IDE Plugin (NB: Classloader security comes into play with the "Subclass generation" approach). Enhancement via javaagent and ant/maven can be a PITA during the development as it slows down the development process (you have to remember to run an ant target or configure a javaagent etc). With "Subclass generation" you can just code and run which is much easier to develop with.

Personally I found developing with javaagent/ant etc a PITA and that is why I ended up writing an Eclipse IDE Plugin to do the enhancement (so effectively you can just code and run without having to invoke an ant target or configuring a javaagent).

Anyways... thats my opinion - hopefully it helps some folks out there.

Cheers, Rob.

NB: The issue with using Field access with "Subclass generation" is a Java Classloader security issue ... 

Franz Wong replied on Wed, 2009/02/18 - 8:54pm

If you talk about field vs property, then I think bean binding is also one of the considerations. I cannot fire any property change event if I use field. I am not saying property is superior than field. However, we always want to choose.

Robin Bygrave replied on Wed, 2009/02/18 - 9:42pm in response to: Franz Wong

>> I cannot fire any property change event if I use field.

Can you expand on this... aka I don't see why field access stops/doesn't allow this but perhaps I'm missing something.

I don't think you mean ... you want property change events to fire when the ORM sets the values?

I'm thinking you want property change events to fire when application code changes the value via calling a setter method? That being so... whats the problem?

Perhaps a little bit of code would help but I'm missing your point at the moment. 

IMHO if this had been refered to as Field/Property interception rather than Field/Property access there would probably be less confusion over this issue (the way I see it anyway).

Thanks, Rob.

 

Robin Bygrave replied on Wed, 2009/02/18 - 11:22pm in response to: Franz Wong

Something like this?

// ... where firstName is a persistent property in an entity bean

public static final String FIRST_NAME_PROPERTY = "firstName";
public void setFirstName(String firstName) {
    String oldValue = this.firstName;
    this.firstName = firstName;
    firePropertyChange(FIRST_NAME_PROPERTY, oldValue, this.firstName);
}

This will work fine with Field Access/interception... (no problem here that I can see)

Franz Wong replied on Thu, 2009/02/19 - 9:46am

I was just thinking the following.

class Foo {
  private String aaa;
  public String bbb;

  public String getAaa() { return aaa; }
  public void setAaa(String aaa) {
     String old = this.aaa;
     this.aaa = aaa;
     firePropertyChange("Aaa", old, this.aaa);
  }
}
// field access
foo.bbb = "bar";
// property access
foo.setAaa("bar");

So I don't know how to fire property change event for field access, even by your code.

Or do I misunderstand the topic?

Robin Bygrave replied on Thu, 2009/02/19 - 7:11pm in response to: Franz Wong

Personally I believe there is a *LOT* of confusion over the topic ...

Field Access/interception effectively means: The ORM will find the GETFIELD and PUTFIELD byte code operations for persistent fields ... and replace them with the ORM specific interception code.

Property Access/interception effectively means: The ORM will intercept calls to the getter and setter methods ... add in the ORM specific interception code ... and then continue with whatever code you have in the getter/setter.

NB: The ORM must put in this 'interception' to support lazy loading and 'dirty checking'

> So I don't know how to fire property change event for field access, even by your code.

The firePropertyChange() is fired by calling the setAaa() method (any code that calls that method)... and the thing to note is that in this case you would not see any effective difference between field or property access. Well, almost ... the only difference is that with property access... the ORM will use the setAaa() method when loading the bean (assuming the Foo instance being loaded doesn't have any registered listeners at the time you will not notice any difference). NB: With field access/interception the ORM is not going to use the setAaa() method (it almost certainly will have generated its own method to set the value).

 

Looking at the setAaa(String aaa) method (with its 3 lines of code) in more detail:

 

FIELD ACCESS/INTERCEPTION:

line 1. THE GETFIELD is 'intercepted/replaced' by the ORM

(the GETFIELD instruction is replaced by an ORM method that will check for lazy loading before returning the value of the aaa field).

line 2. The PUTFIELD in 'intercepted/replaced' by the ORM

(the PUTFIELD instruction is replaced by an ORM method that will likely do both lazy loading checking and 'dirty checking' ... before setting the new value to the aaa field).

line 3. Either there is a GETFIELD intercepted/replaced in the same way as it is in line 1 or it is potentially optimised out/changed due to line 2 (depends on the actual bytecode generated here) .... and the firePropertyChange() method is called as you would normally expect.

 

PROPERTY ACCESS/INTERCEPTION:

The setAaa() method is called... the ORM has likely added an instruction prior to line 1 which will perform the lazy loading and dirty checking... and then lines 1,2,3 are executed normally.

 

The effect is that calling setAaa() with either Field or Property access... will actually result in the same behaviour (and the firePropertyChange() will behave correctly in both cases).

 

So, if you use Field access/interception for Foo.class ... the firePropertyChange() will work fine. The thing to note with Field access/interception is that it does not matter *WHERE* the GETFIELD and PUTFIELD are in Foo code (getters, setters, other methods etc) ... the ORM will properly handle lazy loading and dirty checking etc (by effectively replacing the GETFIELD/PUTFIELD byte code... with its own method call). Aka - you can't go wrong with Field access!!

With Property access/interception... the ORM will only intercept the appropriate getter and setter methods... and only perform the lazy loading and dirty checking on the 'property' associated with the getter and setter... so this means that if you use other persistent fields in your getter/setter ... then you can get some strange behaviour.  Said another way... you can get strange behaviour if you use a persistent field outside of its getter or setter.

 

TO BE FAIR: This is probably more important for Ebean ORM because Ebean supports "Partial Objects"... where you can specify which properties you want to fetch initially (and it will lazy load other fields if they are accessed). e.g. find customer (just get its id, name, billingAddress) ... later ... customer.getStartDate() ... invokes a lazy load of the rest of the customer's properties because it does not have the startDate property loaded).

 

Note: Perhaps useful ... you can get the bytecode outline tool ... http://asm.objectweb.org/eclipse/index.html... which is good for looking at bytecode generated by java code.

 

Hmmm... hope this is helpful.

Cheers, Rob.

Franz Wong replied on Thu, 2009/02/19 - 8:44pm in response to: Robin Bygrave

Your reply is informative. Thanks. :)

Cliff Meyers replied on Mon, 2009/02/23 - 2:05am

Regarding performance, I haven't seen too much difference in some admittedly very basic testing that I've done:

http://stackoverflow.com/questions/332591/performance-difference-between-annotating-fields-or-getter-methods-in-hibernate

Loaded 5000 records into a simple 3 column table. Mapped two classes to that table, one using annotated private fields and another using annotated public getters. Ran 30 runs of Spring's HibernateTemplate.loadAll() followed by a HibernateTemplate.clear() to purge the Session cache. Results in ms below...

methods total: 6510, average: 217

fields total: 6586, average: 219

I should probably take another stab at it after adding more properties to each class but right now the difference doesn't appear to be statistically significant.

 

Mike Keith replied on Fri, 2009/02/27 - 3:18pm in response to: Robin Bygrave

Personally I believe there is a *LOT* of confusion over the topic ...
Heh. You are right. There is a lot of confusion around this topic, and you seem to be contributing to it! Access type has nothing to do with interception, it determines how state is accessed by the underlying ORM engine. If field access is used then the fields can be accessed reflectively, whereas if property access is used then the property methods are invoked to access the state of the entity. You are bringing in two orthogonal issues, lazy loading and change tracking, and explaining ways that lazy loading can be achieved. That's fine (and you did a reasonable job doing it ;-) but not related to access type.

Robin Bygrave replied on Wed, 2009/04/08 - 4:49am in response to: Mike Keith

>  seem to be contributing to it!

Not my intention... hopefully you can clear up the confusion.

> Access type has nothing to do with interception

This certainly seems true for Eclipselink... so in an attempt to clear this up (please correct me) but without any enhancement/weaving eclipselink doesn't support lazy loading at all. 

That is, for non-enhanced beans the behaviour between Hibernate, OpenJPA and EclipseLink differs. The way I see it (again correct me please) is that Hibernate and OpenJPA are using dynamically generated sub-classes to support their behaviour (specifically including support for lazy loading which depends on interception). They can do this by using property access (intercepting public getters and setters)... and they can also support this with Field access but with caveats (the use of Field access depends on classloader security).

So, the way I see it ...  yes, with eclipselink access type really has nothing to do with interception... but for Hibernate and OpenJPA access type does effect interception/lazy loading when using dynamic-subclassing (and classloader security comes into play). 

If I got that correct, then the question becomes then, when using enhanced/weaved entity beans... why would someone chose property access over field access?

Can you detail the pros and cons from your perspective?

 

Thanks, Rob.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.