Why should anybody put annotations on the getters

2019-01-13 05:48发布

问题:

Subject says it all... I see no advantage of people declaring annotations on the getters and/or setters so far. For me this only has the disadvantage of spreading the annotations over the class, which can make the class more unreadable.

Putting the annotations on the fields clearly reduces the amount of code to post when needing help. This is just a tiny advantage though. But putting annotations on methods would serve no purpose to me.

回答1:

Putting annotations on methods forces JPA to access properties via methods. It makes sense when internal state of your object differs from the database schema:

@Entity
public class Employee {
    private String firstName;
    private String lastName;

    @Column(name = "EMP_NAME") // Due to legacy database schema
    public String getName() {
        return fisrtName + " " + lastName;
    }

    public void setName(String name) {
        ...
    }

    ... Getters and setters for firstName and lastName with @Transient ...
}

In JPA 2.0 you can specify access type at fine-grained level with @Access:

@Entity @Access(AccessType.FIELD)
public class Employee {
    @Access(AccessType.PROPERTY) @Column(name = "EMP_NAME")
    public String getName() { ... }
    ... other properties have field access ...
}


回答2:

Why should anybody put annotations on the getters or setters when using JPA to map the classes?

As already mentioned, using property access allows to add logic in the getter, if the need arises.

But since the question is tagged hibernate, I'll mention another (huge) benefit: property access allows you to call foo.getId() without initializing a proxy. You cannot get the same behavior when using field access. Emmanuel Bernard explains this limitation of field access as follows:

That is unfortunate but expected. That's one of the limitations of field level access. Basically we have no way to know that getId() indeed only go and access the id field. So we need to load the entire object to be safe.

So yes, using property access makes the code harder to read, you have for example to browse a whole class to see if there are any @Transient around there. But for me, the benefit (at least with hibernate) outweighs this disadvantage by far.

Related questions

  • Hibernate Annotations - Which is better, field or property access?
  • Hibernate generating SQL queries when accessing associated entity's id

References

  • Proxy loaded on getId-call when using annotations on fields
  • proxy getId => why sql is generated !
  • HHH-3718


回答3:

The answers given are correct. Annotating methods instead of properties gives you:

  1. The right to use getId(), if it's marked as the @Id value, to get a foreign key value from a proxy object without actually loading it from the DB.

  2. You can create getters/setters that update internal object state that is not in the database. I've used this when retrieving compressed state from the DB that I want to decompress within the object into a more usable internal member datum. The setters and getters set and get the compressed state, and the DB and Hibernate don't "know" about the uncompressed internal member.

There is one drawback I've hit:

A. Your setters have to be pretty simple. Hibernate expects them to do what would be accomplished by direct assignment to a member datum. A "setCategory" method that not only sets a category, but also updates the relevant Category object to show the relationship, may get you into trouble.



回答4:

I am using annotations on the getters/setters because I have an API separated from the implementation, and I wanted to keep the API part completely framework-free, allowing me to switch frameworks or provide different implementations. For instance, right now I'm using spring-data-jpa, but with the API below I can easily switch to spring-jdbc or any other framework.

What I did was define the interfaces for the controller, repository and the entity, as such:

public interface MyEntityController<T extends MyEntity> {
    Iterable<T> listEntities();
    T getEntity(Long id);
}

public interface MyEntityService<T extends MyEntity> {
    Iterable<T> findAll();
    T findById(Long id);
}

public interface MyEntityRepository<T extends MyEntity> {
    Iterable<T> findAll();
    T findOne(Long id);
}

// no JPA annotations here
public abstract class MyEntity {
    protected Long id;
    protected String myField;
}

Next I just implement the MyEntity as follows, and use the MyEntityImpl for the Controller, Service and Repository implementations:

@Entity
public class MyEntityImpl extends MyEntity {
    @Id public long getId() { return id; }
    @Column public String getMyField() { return myField };
    // setters, etc
}

@Repository
public interface MyEntityRepositoryImpl extends MyEntityRepository, JPARepository<MyEntityImpl, Long> {
}

I have already tested it and it works fine. Just annotating the MyEntityImpl with @Entity would not have worked, as the superclass would need to be a @MappedSuperclass.