首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何创建支持泛型id (包括自动生成id)的泛型实体模型类?

如何创建支持泛型id (包括自动生成id)的泛型实体模型类?
EN

Stack Overflow用户
提问于 2014-09-29 21:14:51
回答 3查看 10.5K关注 0票数 8

我有三种桌子的主键:

  • INT自动生成的主密钥,它使用来自数据库供应商(MySQL)的AUTO_INCREMENT容量
  • CHAR(X)主键存储用户可读的值作为密钥(其中X是一个数字和50 <= X <= 60)
  • 复杂主键,由表的2或3个字段组成。

此外,还有一些字段可能存在(或不存在):

  • 版本,INT字段。
  • createdBy、VARCHAR(60)字段和lastUpdatedBy、VARCHAR(60)字段(还有更多的字段,但这些字段涵盖了一个基本示例)。

上文的一些例子:

  • Table1
    • id int主键auto_increment
    • 版本int
    • 值字符(10)
    • createdBy varchar(60)
    • lastUpdatedBy varchar(60)

  • Table2
    • id char(60)主键
    • shortDescription varchar(20)
    • longDescription varchar(100)

  • Table3
    • field1 int主键
    • field2 int主键
    • 小数(10,5)
    • 版本int

考虑到所有这些,我需要创建一组泛型类,它支持这些需求,并允许使用Hibernate 4.3和JPA2.1进行CRUD操作。

下面是我的当前模型(为了缩短代码示例,避免了getter/setters):

代码语言:javascript
复制
@MappedSuperclass
public abstract class BaseEntity<T> implements Serializable {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    protected T id;
}

@MappedSuperclass
public abstract class VersionedEntity<T> extends BaseEntity<T> {
    @Version
    protected int version;
}

@MappedSuperclass
public abstract class MaintainedEntity<T> extends VersionedEntity<T> {
    @Column
    protected String createdBy;
    @Column
    protected String lastUpdatedBy;
}

@Entity
public class Table1 extends MaintainedEntity<Long> {
    @Column
    private String value;
}

@Entity
public class Table2 extends BaseEntity<String> {
    @Column
    private String shortDescription;
    @Column
    private String longDescription;
}

我目前正在测试Table1Table2的保存实例。我有以下代码:

代码语言:javascript
复制
SessionFactory sf = HibernateUtils.getSessionFactory();
Session session = sf.getCurrentSession();
session.beginTransaction();

Table1 newTable1 = new Table1();
newTable1.setValue("foo");
session.save(newTable1); //works

Table2 newTable2 = new Table2();
//here I want to set the ID manually
newTable2.setId("foo_id");
newTable2.setShortDescription("short desc");
newTable2.setLongDescription("long description");
session.save(newTable2); //fails

session.getTransaction().commit();
sf.close();

当试图保存Table2时,它会失败,我得到以下错误:

代码语言:javascript
复制
Caused by: java.sql.SQLException: Field 'id' doesn't have a default value
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:996)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3887)

错误消息很明显,因为CHAR(X)字段没有默认值,也不会有默认值(AFAIK)。我尝试将生成策略更改为GenerationType.AUTO,并得到了相同的错误消息。

为了支持这些需求,我如何重新建模这些类?或者更好的是,我如何提供一个取决于我所保存的实体的键的生成策略,它可以由我自动生成或提供?

涉及的技术:

  • Java 8
  • 冬眠4.3.6
  • JPA 2.1
  • MySQL和Postgres数据库
  • 操作系统: Windows 7专业人员

注意:为了支持JPA2.1的其他实现(比如EclipseLink ),上面的内容可能(而且可能会)发生变化。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2014-10-09 06:09:27

您可以“解决”这种强制派生类实现方法的问题,该方法将确保分配Id,并使用@PrePersist对该方法进行注释。您可以为将自动生成Id的类提供默认实现。

就像:

代码语言:javascript
复制
@MappedSuperclass
public abstract class BaseEntity<T> implements Serializable {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    protected T id;

    @PrePersist
    public void ensureIdAssigned() {
          ensureIdAssignedInternal();  
    }


    public abstract void ensureIdAssignedInternal();  
}


@MappedSuperclass
public abstract class AutoIdMaintaintedEntity<T> extends MaintainedEntity<T> { // provide default implementation for Entities with Id generated by @GeneratedValue(strategy=GenerationType.IDENTITY) on BaseEntity superclass
    public void ensureIdAssignedInternal() {
        // nothing here since the Id will be automatically assigned
    }
}

@Entity
public class Table1 extends AutoIdMaintaintedEntity<Long> {
    @Column
    private String value;
}

@Entity
public class Table2 extends BaseEntity<String> {
    @Column
    private String shortDescription;
    @Column
    private String longDescription;

    public void ensureIdAssignedInternal() {
         this.id = generateMyTextId();

    }

     private String generateMyTextId() {
         return "text id";
     }


}
票数 1
EN

Stack Overflow用户

发布于 2014-10-02 06:06:17

没有尝试这一点,但是根据Hibernate的api,这不应该通过创建IdentityGenerator的自定义实现而变得复杂。

它是生成要为其生成值的方法gets和对象,这样您就可以检查id字段的类型并为您的主键返回适当的值。

代码语言:javascript
复制
public class DynamicGenerator  implements IdentityGenerator

        public Serializable generate(SessionImplementor session, Object object)
                throws HibernateException {

             if (shouldUseAutoincrementStartegy(object)) { // basing on object detect if this should be autoincrement or not, for example inspect the type of id field by using reflection - if the type is Integer use IdentityGenerator, otherwise another generator 
                 return new IdentityGenerator().generate(seession, object)
             } else { // else if (shouldUseTextKey)

                 String textKey = generateKey(session, object); // generate key for your object

                 // you can of course connect to database here and execute statements if you need:
                 // Connection connection = session.connection();
                 //  PreparedStatement ps = connection.prepareStatement("SELECT nextkey from text_keys_table");
                 // (...)

                 return textKey;

            }

        }
    }

让它简单地用作您的生成策略:

代码语言:javascript
复制
@MappedSuperclass
public abstract class BaseEntity<T> implements Serializable {
    @Id
    @GenericGenerator(name="seq_id", strategy="my.package.DynamicGenerator")
    protected T id;
}

对于Hibernate 4,您应该实现IdentifierGenerator接口。

如前所述,Hibernate应该仍然可以为任何"jpa兼容“提供程序以更通用的方式创建它。根据GeneratedValue注释中的JPA,您可以提供自定义生成器。这意味着您可以提供自定义生成器的名称,并且应该为每个jpa提供程序实现这个生成器。

这意味着您需要用下面的注释对BaseEntity进行注释

代码语言:javascript
复制
@MappedSuperclass
public abstract class BaseEntity<T> implements Serializable {
    @Id
    @GeneratedValue(generator="my-custom-generator")
    protected T id;
}

现在,您需要为您想使用的每个jpa提供程序注册名为“my-定制生成器”的自定义生成器。

对于Hibernate,这是由@GenericGenerator注释完成的,如前面所示(将@GenericGenerator(name="my-custom-generator", strategy="my.package.DynamicGenerator"添加到id字段或BaseEntity类级别上的BaseEntity类就足够了)。

在EclipseLink中,我看到您可以通过GeneratedValue注释完成此操作,并通过SessionCustomizer注册它:

代码语言:javascript
复制
            properties.put(PersistenceUnitProperties.SESSION_CUSTOMIZER,
                    "my.custom.CustomIdGenerator");

public class CustomIdGenerator extends Sequence implements SessionCustomizer {


    @Override
    public Object getGeneratedValue(Accessor accessor,
            AbstractSession writeSession, String seqName) {
        return  "Id"; // generate the id
    }

    @Override
    public Vector getGeneratedVector(Accessor accessor,
            AbstractSession writeSession, String seqName, int size) {
        return null;
    }

    @Override
    protected void onConnect() {
    }

    @Override
    protected void onDisconnect() {
    }

    @Override
    public boolean shouldAcquireValueAfterInsert() {
        return false;
    }

    @Override
    public boolean shouldOverrideExistingValue(String seqName,
            Object existingValue) {
        return ((String) existingValue).isEmpty();
    }

    @Override
    public boolean shouldUseTransaction() {
        return false;
    }

    @Override
    public boolean shouldUsePreallocation() {
        return false;
    }

    public void customize(Session session) throws Exception {
        CustomIdGenerator sequence = new CustomIdGenerator ("my-custom-generator");

        session.getLogin().addSequence(sequence);
    }

}    

每个提供者必须给出一种注册id生成器的方法,因此如果要支持所有这些生成器,则需要为每个提供程序实现和注册自定义生成策略。

票数 5
EN

Stack Overflow用户

发布于 2014-10-08 22:56:36

继承层次结构对抗ORM。因此,保持简单,保持离数据库实现更近一点。不要为此映射抽象超类的层次结构,而是为共享列的块嵌入带注释的POJO。在您的代码的其余部分中,它们也很可能是结果证明使用起来很方便

为共享字段创建@Embeddable类,并为复合ID @Embeddable创建该类。

代码语言:javascript
复制
@Embeddable
public class Maintained implements Serializable{
    private String maintainedBy;
    private String updatedBy;
    // getters and setters
}

@Embeddable
public class CompositeId implements Serializable{
    @Column
    private int id1;
    @Column
    private int id2;
    ...
}

那么,实现类的最简单版本如下所示:

代码语言:javascript
复制
@Entity
public class Table1 {
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Id
    protected Long id;
    @Version
    private String version;
    @Embedded
    private Maintained maintained;
    ...
    public Maintained getMaintained(){
        return maintained;
    }
}

对于字符串ID,没有自动生成:

代码语言:javascript
复制
@Entity
public class Table2 {
    @Id
    private String id;
    @Column
    private String shortDescription;
    @Column
    private String longDescription;
    ...
}

和组合ID作为@EmbeddedId

代码语言:javascript
复制
@Entity
public class Table3 { 
    @EmbeddedId 
    private CompositeId id;
    @Version
    private String version;
    @Column
    private int amount;
    ...
}

作为一个额外的好处,您可以混合和匹配添加更多的这些特性,如果您愿意,因为您不再受到继承树的限制。

(但您可以将包含getter和setter以及默认委托的层次结构保持在适当的位置,如果现有代码依赖于它并/或从中受益。)

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/26109041

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档