Hibernate Annotations - Which is better, field or property access?

后端 未结 25 1208
说谎
说谎 2020-11-22 15:02

This question is somewhat related to Hibernate Annotation Placement Question.

But I want to know which is better? Access via properties or access vi

相关标签:
25条回答
  • 2020-11-22 15:22

    Normally beans are POJO, so they have accessors anyway.

    So the question is not "which one is better?", but simply "when to use field access?". And the answer is "when you don't need a setter/getter for the field!".

    0 讨论(0)
  • 2020-11-22 15:23

    I prefer using field access for the following reasons:

    1. The property access can lead to very nasty bugs when implementing equals/hashCode and referencing fields directly (as opposed through their getters). This is because the proxy is only initialized when the getters are accessed, and a direct-field access would simply return null.

    2. The property access requires you to annotate all utility methods (e.g. addChild/removeChild) as @Transient.

    3. With field access we can hide the @Version field by not exposing a getter at all. A getter can also lead to adding a setter as well, and the version field should never be set manually (which can lead to very nasty issues). All version incrementation should be triggered through OPTIMISTIC_FORCE_INCREMENT or PESSIMISTIC_FORCE_INCREMENT explicit locking.

    0 讨论(0)
  • 2020-11-22 15:23

    By default, JPA providers access the values of entity fields and map those fields to database columns using the entity’s JavaBean property accessor (getter) and mutator (setter) methods. As such, the names and types of the private fields in an entity do not matter to JPA. Instead, JPA looks at only the names and return types of the JavaBean property accessors. You can alter this using the @javax.persistence.Access annotation, which enables you to explicitly specify the access methodology that the JPA provider should employ.

    @Entity
    @Access(AccessType.FIELD)
    public class SomeEntity implements Serializable
    {
    ...
    }
    

    The available options for the AccessType enum are PROPERTY (the default) and FIELD. With PROPERTY, the provider gets and sets field values using the JavaBean property methods. FIELD makes the provider get and set field values using the instance fields. As a best practice, you should just stick to the default and use JavaBean properties unless you have a compelling reason to do otherwise.

    You can put these property annotations on either the private fields or the public accessor methods. If you use AccessType.PROPERTY (default) and annotate the private fields instead of the JavaBean accessors, the field names must match the JavaBean property names. However, the names do not have to match if you annotate the JavaBean accessors. Likewise, if you use AccessType.FIELD and annotate the JavaBean accessors instead of the fields, the field names must also match the JavaBean property names. In this case, they do not have to match if you annotate the fields. It’s best to just be consistent and annotate the JavaBean accessors for AccessType.PROPERTY and the fields for AccessType.FIELD.

    It is important that you should never mix JPA property annotations and JPA field annotations in the same entity. Doing so results in unspecified behavior and is very likely to cause errors.

    0 讨论(0)
  • 2020-11-22 15:23

    We created entity beans and used getter annotations. The problem we ran into is this: some entities have complex rules for some properties regarding when they can be updated. The solution was to have some business logic in each setter that determines whether or not the actual value changed and, if so, whether the change should be allowed. Of course, Hibernate can always set the properties, so we ended up with two groups of setters. Pretty ugly.

    Reading previous posts, I also see that referencing the properties from inside the entity could lead to issues with collections not loading.

    Bottom line, I would lean toward annotating the fields in the future.

    0 讨论(0)
  • 2020-11-22 15:24

    I think annotating the property is better because updating fields directly breaks encapsulation, even when your ORM does it.

    Here's a great example of where it will burn you: you probably want your annotations for hibernate validator & persistence in the same place (either fields or properties). If you want to test your hibernate validator powered validations which are annotated on a field, you can't use a mock of your entity to isolate your unit test to just the validator. Ouch.

    0 讨论(0)
  • 2020-11-22 15:26

    I believe property access vs. field access is subtly different with regards to lazy initialisation.

    Consider the following mappings for 2 basic beans:

    <hibernate-mapping package="org.nkl.model" default-access="field">
      <class name="FieldBean" table="FIELD_BEAN">
        <id name="id">
          <generator class="sequence" />
        </id>
        <property name="message" />
      </class>
    </hibernate-mapping>
    
    <hibernate-mapping package="org.nkl.model" default-access="property">
      <class name="PropBean" table="PROP_BEAN">
        <id name="id">
          <generator class="sequence" />
        </id>
        <property name="message" />
      </class>
    </hibernate-mapping>
    

    And the following unit tests:

    @Test
    public void testFieldBean() {
        Session session = sessionFactory.openSession();
        Transaction tx = session.beginTransaction();
        FieldBean fb = new FieldBean("field");
        Long id = (Long) session.save(fb);
        tx.commit();
        session.close();
    
        session = sessionFactory.openSession();
        tx = session.beginTransaction();
        fb = (FieldBean) session.load(FieldBean.class, id);
        System.out.println(fb.getId());
        tx.commit();
        session.close();
    }
    
    @Test
    public void testPropBean() {
        Session session = sessionFactory.openSession();
        Transaction tx = session.beginTransaction();
        PropBean pb = new PropBean("prop");
        Long id = (Long) session.save(pb);
        tx.commit();
        session.close();
    
        session = sessionFactory.openSession();
        tx = session.beginTransaction();
        pb = (PropBean) session.load(PropBean.class, id);
        System.out.println(pb.getId());
        tx.commit();
        session.close();
    }
    

    You will see the subtle difference in the selects required:

    Hibernate: 
        call next value for hibernate_sequence
    Hibernate: 
        insert 
        into
            FIELD_BEAN
            (message, id) 
        values
            (?, ?)
    Hibernate: 
        select
            fieldbean0_.id as id1_0_,
            fieldbean0_.message as message1_0_ 
        from
            FIELD_BEAN fieldbean0_ 
        where
            fieldbean0_.id=?
    0
    Hibernate: 
        call next value for hibernate_sequence
    Hibernate: 
        insert 
        into
            PROP_BEAN
            (message, id) 
        values
            (?, ?)
    1
    

    That is, calling fb.getId() requires a select, whereas pb.getId() does not.

    0 讨论(0)
提交回复
热议问题