基于Hibernate生成标识符@SequenceGenerator注释

时间:2017-05-15 15:17:38

标签: java hibernate jpa

我一直在寻找一种生成ID的方法,而不会在Hibernate中实际持久化实体。原因是我想手动插入新的数据库行。理想的情况是使用MySQL中存在的自动增量,但由于我使用的是InheritanceType.TABLE_PER_CLASS,这是不可能的。 (无法切换继承映射或序列生成的策略,因为该项目已经相当成熟。)

我所做研究的结论是(如果我错了,请纠正我):

  • Hibernate通过将next_val增加1来保留ID块.Hibernate可以分配给实体的ID范围,而不必再次查询和更新序列表,是(使用旧值next_val) next_val * allocationSize ...(next_val + 1)* allocationSize

我从结论中得出的结论:

  • Hibernate生成的ID远远 next_val * allocationSize (与结论相冲突) - 事实上,似乎Hibernate使用了ID到 next_val * allocationSize (但不完全:next_val的值是350781,而数据库中的最高ID是350744,allocationSize为20)
  • 有时,next_val的值小于,而不是数据库中的最高ID

这让我有了将ID生成过程委托给Hibernate的唯一选择,因为我不清楚如何生成ID。除了我不清楚的事实,我想有一个适用于不同ID生成策略的解决方案。

所以我想采取的方法是从java代码生成一堆id,然后为我“保留”(包含next_val的支持表得到更新),所以我可以在我的INSERT查询中使用它们。

这看起来像这样:

给出以下定义:

@Entity
@Table(name="datamodel")
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public abstract class DataModel {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator="model_sequence")
    @SequenceGenerator(
        name="model_sequence",
        sequenceName="model_sequence",
        allocationSize=20
    )
    @Column(name="id", nullable=false, unique=true, length=11)
    private int id;

}

以这样的方式生成ID:

// This will be the ID generator, a
// long-lived object that generates ids
IDGenerator gen = new IDGenerator();
gen.initialize(DataModel.class, ... /* provide a serviceRegistry, sessionFactory, or anything else that might be needed to initialize this object */);

// This will generate the next ID based
// on the parameters passed to @SequenceGenerator
// and the values that are present in the DB.
int nextId = gen.generateNextId(/* provide a session, transaction, or anything else that might be needed for accessing the DB */);

以下帖子帮助我进一步了解:

我目前的尝试仍然停留在这一点上:

URL configUrl = this.getClass().getClassLoader().getResource("hibernate.main.cfg.xml");
Configuration configuration = new Configuration().configure(configUrl);
ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
SessionFactorysessionFactory = configuration.buildSessionFactory(serviceRegistry);

Properties generatorProperties = new Properties();
// Properties would normally be read from annotations
// that are present on the entity class
generatorProperties.put("name", "model_sequence");
generatorProperties.put("sequence_name", "model_sequence");
generatorProperties.put("allocation_size", 20);

sequenceGenerator = new SequenceStyleGenerator();
sequenceGenerator.configure(LongType.INSTANCE, generatorProperties, serviceRegistry);
// Without the line below, the queries that Hibernate uses to read
// and alter the sequence table are not initialized (they are null)
// and Hibernate throws an exception
sequenceGenerator.registerExportables( database?? /* This requires a Database object, but how to provide it? */);

// In order to generate a new ID, do the following:
Session session = sessionFactory.getCurrentSession();
Serializable id = sequenceGenerator.generate((SessionImplementor)session, entity?? /* This requires an entity, but that is exactly what I'm trying to omit. */);

我对这种方法的考虑:

  • 据我所知,Hibernate保留了一个ID块(而不仅仅是1个ID)。这意味着ID块在SequenceStyleGenerator实例的内存中维护。这将导致两个块同时出现:一个用于运行的Web应用程序,另一个用于我的ID生成应用程序。
  • 当序列表未被锁定时可能会出现问题,在这种情况下,两个实例可能同时从表中读取相同的值,最终使用相同的ID块。

你们有没有想过如何做到这一点?

0 个答案:

没有答案