5

我正在使用该javax.persistence包来映射我的 Java 类。

我有这样的实体:

public class UserEntity extends IdEntity {
}

它扩展了一个名为的映射超类IdEntity

@MappedSuperclass
public class IdEntity extends VersionEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    // Getters and setters below...    

}

IdEntity类扩展了另一个名为的映射超类VersionEntity,以使所有实体都继承版本属性:

@MappedSuperclass
public abstract class VersionEntity {

    @Version
    private Integer version;

    // Getters and setters below...

}

为什么?

因为现在我可以对所有实体的 IdEntity 类进行通用查询,它看起来像这样:(示例)

CriteriaBuilder builder = JPA.em().getCriteriaBuilder();
CriteriaQuery<IdEntity> criteria = builder.createQuery(IdEntity.class);

现在来解决问题。

我的一些实体将具有时间戳,例如created_atdeleted_at。但并非所有实体。

我可以在我的实体类中提供这些属性,如下所示:

public class UserEntity extends IdEntity {

    @Basic(optional = false)
    @Column(name = "updated_at")
    @Temporal(TemporalType.TIMESTAMP)
    private Date updatedAt;
}

但是由于我有很多实体,这将使我在所有应该有时间戳的实体中放置大量冗余代码。我希望有某种方法可以使相关类以某种方式继承这些字段。

一种可能的解决方案是创建一个并行IdEntity超类,可能命名IdAndTimeStampEntity并让那些应该具有时间戳的实体从这个新超类继承,但是嘿,这对我的同事开发人员不公平,因为现在他们必须知道何时选择哪个超类编写通用查询:

CriteriaBuilder builder = JPA.em().getCriteriaBuilder();
CriteriaQuery<???> criteria = builder.createQuery(???); // Hmm which entity should I choose IdEntity or IdAndTimeStampEntity ?? *Annoyed*

并且通用实体查询变得不那么通用..

我的问题:如何让all我的实体继承idversion字段,但只有所有实体的子部分继承时间戳字段,但将我的查询保留为单一类型的实体?

更新#1

来自 Bolzano 的问题:“您可以添加为实体指定路径(保存表信息)的代码吗?”

UserEntity这是查询 a 的工作示例IdEntity

CriteriaBuilder builder = JPA.em().getCriteriaBuilder();
CriteriaQuery<IdEntity> criteria = builder.createQuery(IdEntity.class);
Root<IdEntity> from = criteria.from(IdEntity.class);
criteria.select(from);

Path<Integer> idPath = from.get(UserEntity_.id); //generated meta model
criteria.where(builder.in(idPath).value(id));

TypedQuery<IdEntity> query = JPA.em().createQuery(criteria);
return query.getSingleResult();
4

3 回答 3

5

我会选择一个不强制执行您概述的基于类的对象模型的解决方案。当您不需要乐观并发检查并且没有时间戳,或时间戳但没有 OCC,或者您想要添加的下一个半通用功能时会发生什么?排列将变得难以管理。

我会将这些常见的交互添加为接口,并且我会通过 id 使用泛型增强您的可重用查找,以将您关心的实际类返回给调用者而不是基超类。

注意:我在 Stack Overflow 中编写了这段代码。它可能需要一些调整才能编译。

@MappedSuperclass
public abstract class Persistable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    // getter/setter
}

public interface Versioned {
    Integer getVersion();
}

public interface Timestamped {
    Date getCreated();
    Date getLastUpdated();
}

@Embeddable
public class TimestampedEntity {
    @Column(name = "create_date")
    @Temporal
    private Date created;

    @Column
    @Temporal
    private Date lastUpdated;

    // getters/setters
}

@Entity
public class UserEntity extends Persistable implements Versioned, Timestamped {
    @Version
    private Integer version;

    @Embedded
    private TimestampedEntity timestamps;

    /*
     * interface-defined getters.  getTimestamps() doesn't need to 
     * be exposed separately.
     */
}

public class <CriteriaHelperUtil> {
    public <T extends Persistable> T getEntity(Class<T> clazz, Integer id, SingularAttribute idField) {
        CriteriaBuilder builder = JPA.em().getCriteriaBuilder();
        CriteriaQuery<T> criteria = builder.createQuery(clazz);
        Root<T> from = criteria.from(clazz);
        criteria.select(from);

        Path<Integer> idPath = from.get(idField);
        criteria.where(builder.in(idPath).value(id));

        TypedQuery<T> query = JPA.em().createQuery(criteria);
        return query.getSingleResult();
    }
}

基本用法:

private UserEntity ue = CriteriaHelperUtil.getEntity(UserEntity.class, 1, UserEntity_.id);
ue.getId();
ue.getVersion();
ue.getCreated();

// FooEntity implements Persistable, Timestamped
private FooEntity fe =  CriteriaHelperUtil.getEntity(FooEntity.class, 10, FooEntity_.id);
fe.getId();
fe.getCreated();
fe.getVersion();  // Compile Error!
于 2016-11-30T15:31:04.190 回答
3
@MappedSuperclass
public class IdEntity{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    @Version
    private Integer version;
}   
@MappedSuperclass
public class IdAndTimeStampEntity extends IdEntity{
    Date created;
}
@Entity
public class UserEntity extends IdAndTimeStampEntity{
    String name;
}
@Entity
public class FooEntity extends IdEntity{...

此解决方案的优点:

  1. 以简单明了的方式使用 OOP,无需在每个子类中嵌入实现接口的重复代码。(每个类也是接口)

  2. 乐观锁定版本列是主要使用的方法。并且应该是基类的一部分。除了像代码表这样的只读实体。

用法:

public <T extends IdEntity> T persist(T entity) {

    if (entity instanceof IdAndTimeStampEntity) {
        ((IdAndTimeStampEntity) entity).setCreated(new Date());
    }

    if (!em.contains(entity) && entity.getId() != null) {
        return em.merge(entity);
    } else {
        em.persist(entity);
        return entity;
    }
}
于 2016-11-30T11:09:16.710 回答
2

我希望有某种方法可以使相关类以某种方式继承这些字段。

可以通过使用字节码操作框架或创建委托子类来制作自定义注释@Timed并使用注释处理器添加时间戳字段和注释。或者,例如,如果您使用 Lombok,请创建 Lombok 注释。

这样,您的团队成员只需记住@Timed在您拥有带时间戳的实体时使用注释。您是否喜欢这种方法取决于您。

于 2016-12-01T08:56:14.673 回答