在使用 JPA 映射类时,为啥有人要在 getter 或 setter 上添加注释?

Posted

技术标签:

【中文标题】在使用 JPA 映射类时,为啥有人要在 getter 或 setter 上添加注释?【英文标题】:Why should anybody put annotations on the getters or setters when using JPA to map the classes?在使用 JPA 映射类时,为什么有人要在 getter 或 setter 上添加注释? 【发布时间】:2011-05-10 10:30:53 【问题描述】:

主题说明了一切......到目前为止,我认为人们在 getter 和/或 setter 上声明注释没有任何优势。对我来说,这只是将注释分散到类中的缺点,这会使类更不可读。

在字段上添加注释明显减少了需要帮助时发布的代码量。不过,这只是一个很小的优势。但是在方法上添加注释对我来说毫无用处。

【问题讨论】:

【参考方案1】:

在方法上添加注释会强制 JPA 通过方法访问属性。当对象的内部状态与数据库模式不同时,这是有意义的:

@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 ...

在 JPA 2.0 中,您可以使用 @Access 在细粒度级别指定访问类型:

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

【讨论】:

【参考方案2】:

在使用 JPA 映射类时,为什么要在 getter 或 setter 上添加注释?

如前所述,如果需要,使用属性访问允许在 getter 中添加逻辑。

但由于问题被标记为hibernate,我将提到另一个(巨大)好处:属性访问允许您调用foo.getId()无需初始化代理。使用字段访问时,您无法获得相同的行为。 Emmanuel Bernard 对字段访问的这种限制解释如下:

这很不幸,但在意料之中。这是字段级访问的限制之一。 基本上我们无法知道 getId() 确实只是去访问 id 字段。所以我们需要加载整个对象以保证安全。

所以是的,使用属性访问会使代码更难阅读,例如,您必须浏览整个类以查看周围是否有 @Transient。但对我来说,好处(至少对于hibernate)远远超过了这个缺点。

相关问题

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

参考文献

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

【讨论】:

我认为 setter 上的注释不受支持,因为使用 setter 的反射来确定字段的类类型更加困难。对于 getter,您所要做的就是检查方法的返回类型。但是对于一个setter,如果你给它传递多个参数怎么办?【参考方案3】:

给出的答案是正确的。注释方法而不是属性给你:

    有权使用 getId()(如果它被标记为 @Id 值)从代理对象获取外键值,而无需实际从数据库中加载它。

    您可以创建 getter/setter 来更新不在数据库中的内部对象状态。我在从数据库中检索压缩状态时使用了它,我想在对象中将其解压缩为更可用的内部成员数据。 setter 和 getter 设置和获取压缩状态,而 DB 和 Hibernate 并不“知道”未压缩的内部成员。

我遇到了一个缺点:

A.你的二传手必须非常简单。 Hibernate 期望他们完成通过直接分配给成员数据来完成的事情。一个“setCategory”方法,不仅设置一个类别,而且更新相关的类别对象以显示关系,可能会给你带来麻烦。

【讨论】:

【参考方案4】:

我在 getter/setter 上使用注解,因为我有一个与实现分离的 API,并且我想保持 API 部分完全无框架,允许我切换框架或提供不同的实现。例如,我现在使用的是 spring-data-jpa,但是通过下面的 API,我可以轻松切换到 spring-jdbc 或任何其他框架。

我所做的是为控制器、存储库和实体定义接口,如下所示:

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;

接下来我只实现 MyEntity 如下,并将 MyEntityImpl 用于 Controller、Service 和 Repository 实现:

@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> 

我已经测试过了,它工作正常。仅仅用@Entity 注释MyEntityImpl 是行不通的,因为超类需要是@MappedSuperclass

【讨论】:

以上是关于在使用 JPA 映射类时,为啥有人要在 getter 或 setter 上添加注释?的主要内容,如果未能解决你的问题,请参考以下文章

JPA 表继承和对象映射

JPA 为啥使用 createNamedQuery

C ++为啥在使用类时应该使用get和set函数[重复]

如何在使用 JPA 映射时增加 mysql 中字符串的长度

为啥 JPA 默认使用 FetchType EAGER 来处理 @ManyToOne 关系

为啥人们继续使用 xml 映射文件而不是注释? [关闭]