提问人:Miguel Ping 提问时间:11/10/2008 最后编辑:Miguel Ping 更新时间:8/28/2023 访问量:174241
休眠 JPA 序列(非 ID)
Hibernate JPA Sequence (non-Id)
问:
是否可以对某些不是标识符/不是复合标识符一部分的列使用数据库序列?
我使用 hibernate 作为 jpa 提供程序,并且我有一个表,其中包含一些生成值的列(使用序列),尽管它们不是标识符的一部分。
我想要的是使用序列为实体创建一个新值,其中序列的列不是主键(的一部分):
@Entity
@Table(name = "MyTable")
public class MyEntity {
//...
@Id //... etc
public Long getId() {
return id;
}
//note NO @Id here! but this doesn't work...
@GeneratedValue(strategy = GenerationType.AUTO, generator = "myGen")
@SequenceGenerator(name = "myGen", sequenceName = "MY_SEQUENCE")
@Column(name = "SEQ_VAL", unique = false, nullable = false, insertable = true, updatable = true)
public Long getMySequencedValue(){
return myVal;
}
}
然后当我这样做时:
em.persist(new MyEntity());
将生成 ID,但属性也将由我的 JPA 提供程序生成。mySequenceVal
只是为了明确这一点:我希望 Hibernate 为属性生成值。我知道 Hibernate 可以处理数据库生成的值,但我不想使用触发器或 Hibernate 本身以外的任何其他东西来为我的财产生成值。如果 Hibernate 可以生成主键的值,为什么它不能为一个简单的属性生成值?mySequencedValue
答:
我遇到过像您这样的情况(非@Id字段的 JPA/Hibernate 序列),我最终在我的数据库架构中创建了一个触发器,该触发器在插入时添加了唯一的序列号。我从来没有让它与 JPA/Hibernate 一起使用
Hibernate 绝对支持这一点。从文档中:
“生成的属性是其值由数据库生成的属性。通常,Hibernate 应用程序需要刷新包含数据库为其生成值的任何属性的对象。但是,将属性标记为已生成,则允许应用程序将此责任委派给 Hibernate。从本质上讲,每当 Hibernate 为定义了生成属性的实体发出 SQL INSERT 或 UPDATE 时,它都会立即发出 select 以检索生成的值。
对于仅在插入时生成的属性,属性映射 (.hbm.xml) 将如下所示:
<property name="foo" generated="insert"/>
对于在插入和更新时生成的属性,属性映射 (.hbm.xml) 将如下所示:
<property name="foo" generated="always"/>
不幸的是,我不了解 JPA,所以我不知道这个功能是否通过 JPA 公开(我怀疑可能不是)
或者,您应该能够从插入和更新中排除该属性,然后“手动”调用 session.refresh( obj );插入/更新它以从数据库加载生成的值之后。
这是在插入和更新语句中排除该属性的方法:
<property name="foo" update="false" insert="false"/>
同样,我不知道 JPA 是否公开了这些 Hibernate 功能,但 Hibernate 确实支持它们。
评论
“我不想使用触发器或其他任何东西来为 Hibernate 本身生成价值”
在这种情况下,如何创建生成所需值的 UserType 实现,并将元数据配置为使用该 UserType 来持久化 mySequenceVal 属性?
我和你一样在同样的情况下运行,我也没有找到任何严肃的答案,如果基本上可以使用 JPA 生成非 id 属性。
我的解决方案是使用本机 JPA 查询调用序列,以便在强制执行之前手动设置属性。
这并不令人满意,但它目前可以作为解决方法。
马里奥
为了寻找这个问题的答案,我偶然发现了这个链接
Hibernate/JPA 似乎无法自动为您的非 id-properties 创建值。注释仅用于创建自动编号。@GeneratedValue
@Id
注解只是告诉 Hibernate 数据库正在生成此值。@GeneratedValue
该论坛中建议的解决方案(或解决方法)是使用生成的 ID 创建一个单独的实体,如下所示:
@Entity public class GeneralSequenceNumber { @Id @GeneratedValue(...) private Long number; } @Entity public class MyEntity { @Id .. private Long id; @OneToOne(...) private GeneralSequnceNumber myVal; }
评论
@GeneratedValue
这与使用序列不同。使用序列时,不会插入或更新任何内容。您只是在检索下一个序列值。看起来 hibernate 不支持它。
作为后续工作,以下是我如何让它工作:
@Override public Long getNextExternalId() {
BigDecimal seq =
(BigDecimal)((List)em.createNativeQuery("select col_msd_external_id_seq.nextval from dual").getResultList()).get(0);
return seq.longValue();
}
评论
SQLQuery sqlQuery = getSession().createSQLQuery("select NAMED_SEQ.nextval seq from dual"); sqlQuery.addScalar("seq", LongType.INSTANCE); return (Long) sqlQuery.uniqueResult();
我在 JPA 规范的会话 9.1.9 GeneratedValue Annotation 中找到了这个具体说明: “[43] 可移植应用程序不应在其他持久性字段或属性上使用 GeneratedValue 注释。 因此,我认为至少仅使用JPA不可能自动生成非主键值的值。
我发现这很完美,但仅适用于 PostgreSQL。对我来说,这是一个完美的解决方案,因为第二个实体是“丑陋”的选择。@Column(columnDefinition="serial")
对实体的调用也是必要的,并且不足以填充数据库中的值。saveAndFlush
save
评论
columnDefinition=
mycolumn serial
@Column(columnDefinition = "integer auto_increment")
@Column(insertable = false, updatable = false, columnDefinition="serial")
虽然这是一个旧线程,但我想分享我的解决方案,并希望得到一些反馈。请注意,我只在一些 JUnit 测试用例中使用我的本地数据库测试了这个解决方案。因此,到目前为止,这不是一个富有成效的功能。
我通过引入一个名为 Sequence 的自定义注释解决了这个问题,没有属性。它只是应从递增序列中分配值的字段的标记。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Sequence
{
}
使用此注释,我标记了我的实体。
public class Area extends BaseEntity implements ClientAware, IssuerAware
{
@Column(name = "areaNumber", updatable = false)
@Sequence
private Integer areaNumber;
....
}
为了保持数据库的独立性,我引入了一个名为 SequenceNumber 的实体,它保存序列当前值和增量大小。我选择了 className 作为唯一键,因此每个实体类都会获得自己的序列。
@Entity
@Table(name = "SequenceNumber", uniqueConstraints = { @UniqueConstraint(columnNames = { "className" }) })
public class SequenceNumber
{
@Id
@Column(name = "className", updatable = false)
private String className;
@Column(name = "nextValue")
private Integer nextValue = 1;
@Column(name = "incrementValue")
private Integer incrementValue = 10;
... some getters and setters ....
}
最后一步也是最困难的是处理序列号分配的 PreInsertListener。请注意,我使用 spring 作为 bean 容器。
@Component
public class SequenceListener implements PreInsertEventListener
{
private static final long serialVersionUID = 7946581162328559098L;
private final static Logger log = Logger.getLogger(SequenceListener.class);
@Autowired
private SessionFactoryImplementor sessionFactoryImpl;
private final Map<String, CacheEntry> cache = new HashMap<>();
@PostConstruct
public void selfRegister()
{
// As you might expect, an EventListenerRegistry is the place with which event listeners are registered
// It is a service so we look it up using the service registry
final EventListenerRegistry eventListenerRegistry = sessionFactoryImpl.getServiceRegistry().getService(EventListenerRegistry.class);
// add the listener to the end of the listener chain
eventListenerRegistry.appendListeners(EventType.PRE_INSERT, this);
}
@Override
public boolean onPreInsert(PreInsertEvent p_event)
{
updateSequenceValue(p_event.getEntity(), p_event.getState(), p_event.getPersister().getPropertyNames());
return false;
}
private void updateSequenceValue(Object p_entity, Object[] p_state, String[] p_propertyNames)
{
try
{
List<Field> fields = ReflectUtil.getFields(p_entity.getClass(), null, Sequence.class);
if (!fields.isEmpty())
{
if (log.isDebugEnabled())
{
log.debug("Intercepted custom sequence entity.");
}
for (Field field : fields)
{
Integer value = getSequenceNumber(p_entity.getClass().getName());
field.setAccessible(true);
field.set(p_entity, value);
setPropertyState(p_state, p_propertyNames, field.getName(), value);
if (log.isDebugEnabled())
{
LogMF.debug(log, "Set {0} property to {1}.", new Object[] { field, value });
}
}
}
}
catch (Exception e)
{
log.error("Failed to set sequence property.", e);
}
}
private Integer getSequenceNumber(String p_className)
{
synchronized (cache)
{
CacheEntry current = cache.get(p_className);
// not in cache yet => load from database
if ((current == null) || current.isEmpty())
{
boolean insert = false;
StatelessSession session = sessionFactoryImpl.openStatelessSession();
session.beginTransaction();
SequenceNumber sequenceNumber = (SequenceNumber) session.get(SequenceNumber.class, p_className);
// not in database yet => create new sequence
if (sequenceNumber == null)
{
sequenceNumber = new SequenceNumber();
sequenceNumber.setClassName(p_className);
insert = true;
}
current = new CacheEntry(sequenceNumber.getNextValue() + sequenceNumber.getIncrementValue(), sequenceNumber.getNextValue());
cache.put(p_className, current);
sequenceNumber.setNextValue(sequenceNumber.getNextValue() + sequenceNumber.getIncrementValue());
if (insert)
{
session.insert(sequenceNumber);
}
else
{
session.update(sequenceNumber);
}
session.getTransaction().commit();
session.close();
}
return current.next();
}
}
private void setPropertyState(Object[] propertyStates, String[] propertyNames, String propertyName, Object propertyState)
{
for (int i = 0; i < propertyNames.length; i++)
{
if (propertyName.equals(propertyNames[i]))
{
propertyStates[i] = propertyState;
return;
}
}
}
private static class CacheEntry
{
private int current;
private final int limit;
public CacheEntry(final int p_limit, final int p_current)
{
current = p_current;
limit = p_limit;
}
public Integer next()
{
return current++;
}
public boolean isEmpty()
{
return current >= limit;
}
}
}
从上面的代码中可以看出,侦听器对每个实体类使用一个 SequenceNumber 实例,并保留了几个由 SequenceNumber 实体的 incrementValue 定义的序列号。如果序列号用完,它将加载目标类的 SequenceNumber 实体,并为下一次调用保留 incrementValue 值。这样,我就不需要在每次需要序列值时都查询数据库。 请注意为保留下一组序列号而打开的 StatelessSession。不能使用当前保留目标实体的同一会话,因为这会导致 EntityPersister 中出现 ConcurrentModificationException。
希望这对某人有所帮助。
我知道这是一个非常古老的问题,但它首先在结果中显示出来,自从这个问题以来,jpa 发生了很大变化。
现在正确的方法是使用注释。您可以定义序列,将列中的默认值设置为该序列,然后将列映射为:@Generated
@Generated(GenerationTime.INSERT)
@Column(name = "column_name", insertable = false)
评论
我使用注释修复了 Hibernate 的 UUID(或序列)的生成:@PrePersist
@PrePersist
public void initializeUUID() {
if (uuid == null) {
uuid = UUID.randomUUID().toString();
}
}
评论
花了几个小时后,这巧妙地帮助我解决了我的问题:
对于 Oracle 12c:
ID NUMBER GENERATED as IDENTITY
对于 H2:
ID BIGINT GENERATED as auto_increment
同时制作:
@Column(insertable = false)
如果您使用的是 postgresql,
而我在 spring boot 1.5.6 中使用
@Column(columnDefinition = "serial")
@Generated(GenerationTime.INSERT)
private Integer orderID;
评论
seq_order
nextval('seq_order'::regclass)
看起来线程很旧,我只是想在这里添加我的解决方案(在春天使用 AspectJ - AOP)。
解决方案是按如下方式创建自定义注释。@InjectSequenceValue
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface InjectSequenceValue {
String sequencename();
}
现在,您可以对实体中的任何字段进行注释,以便在运行时使用序列的 nextvalue 注入基础字段(长整型/整数)值。
像这样注释。
//serialNumber will be injected dynamically, with the next value of the serialnum_sequence.
@InjectSequenceValue(sequencename = "serialnum_sequence")
Long serialNumber;
到目前为止,我们已经标记了需要注入序列值的字段。因此,我们将研究如何将序列值注入标记的字段,这是通过在 AspectJ 中创建点切割来完成的。
我们将在执行方法之前触发注入。这是在下面的类中完成的。save/persist
@Aspect
@Configuration
public class AspectDefinition {
@Autowired
JdbcTemplate jdbcTemplate;
//@Before("execution(* org.hibernate.session.save(..))") Use this for Hibernate.(also include session.save())
@Before("execution(* org.springframework.data.repository.CrudRepository.save(..))") //This is for JPA.
public void generateSequence(JoinPoint joinPoint){
Object [] aragumentList=joinPoint.getArgs(); //Getting all arguments of the save
for (Object arg :aragumentList ) {
if (arg.getClass().isAnnotationPresent(Entity.class)){ // getting the Entity class
Field[] fields = arg.getClass().getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(InjectSequenceValue.class)) { //getting annotated fields
field.setAccessible(true);
try {
if (field.get(arg) == null){ // Setting the next value
String sequenceName=field.getAnnotation(InjectSequenceValue.class).sequencename();
long nextval=getNextValue(sequenceName);
System.out.println("Next value :"+nextval); //TODO remove sout.
field.set(arg, nextval);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
}
/**
* This method fetches the next value from sequence
* @param sequence
* @return
*/
public long getNextValue(String sequence){
long sequenceNextVal=0L;
SqlRowSet sqlRowSet= jdbcTemplate.queryForRowSet("SELECT "+sequence+".NEXTVAL as value FROM DUAL");
while (sqlRowSet.next()){
sequenceNextVal=sqlRowSet.getLong("value");
}
return sequenceNextVal;
}
}
现在,您可以按如下方式注释任何实体。
@Entity
@Table(name = "T_USER")
public class UserEntity {
@Id
@SequenceGenerator(sequenceName = "userid_sequence",name = "this_seq")
@GeneratedValue(strategy = GenerationType.SEQUENCE,generator = "this_seq")
Long id;
String userName;
String password;
@InjectSequenceValue(sequencename = "serialnum_sequence") // this will be injected at the time of saving.
Long serialNumber;
String name;
}
如果插入时需要具有 UNIQUEIDENTIFIER 类型和默认生成的列,但列不是 PK
@Generated(GenerationTime.INSERT)
@Column(nullable = false , columnDefinition="UNIQUEIDENTIFIER")
private String uuidValue;
在 db 中,您将拥有
CREATE TABLE operation.Table1
(
Id INT IDENTITY (1,1) NOT NULL,
UuidValue UNIQUEIDENTIFIER DEFAULT NEWID() NOT NULL)
在这种情况下,您不会为您需要的值定义生成器(这将自动感谢 )。您可以尝试其他列类型columnDefinition="UNIQUEIDENTIFIER"
我在 Spring 应用程序中使用 @PostConstruct 和 JdbcTemplate 在 MySql 数据库上找到了解决方法。它可能适用于其他数据库,但我将介绍的用例是基于我对 MySql 的经验,因为它使用auto_increment。
首先,我尝试使用 @Column 注释的 ColumnDefinition 属性将列定义为auto_increment,但它不起作用,因为该列需要是一个键才能自动增量,但显然该列在定义之前不会被定义为索引,导致死锁。
在这里,我提出了在没有auto_increment定义的情况下创建列的想法,并在创建数据库后添加它。这可以通过 @PostConstruct 注解来实现,它会导致在应用程序初始化 bean 后立即调用方法,并结合 JdbcTemplate 的 update 方法。
代码如下:
在我的实体中:
@Entity
@Table(name = "MyTable", indexes = { @Index(name = "my_index", columnList = "mySequencedValue") })
public class MyEntity {
//...
@Column(columnDefinition = "integer unsigned", nullable = false, updatable = false, insertable = false)
private Long mySequencedValue;
//...
}
在 PostConstructComponent 类中:
@Component
public class PostConstructComponent {
@Autowired
private JdbcTemplate jdbcTemplate;
@PostConstruct
public void makeMyEntityMySequencedValueAutoIncremental() {
jdbcTemplate.update("alter table MyTable modify mySequencedValue int unsigned auto_increment");
}
}
我想在 @Morten Berg 接受的解决方案旁边提供一个替代方案,这对我来说效果更好。
这种方法允许使用实际需要的类型定义字段 - 在我的用例中 - 而不是 .这可能很有用,例如对于JSON(反)序列化。Number
Long
GeneralSequenceNumber
缺点是它需要更多的数据库开销。
首先,我们需要一个我们想要自动递增类型的:ActualEntity
generated
Long
// ...
@Entity
public class ActualEntity {
@Id
// ...
Long id;
@Column(unique = true, updatable = false, nullable = false)
Long generated;
// ...
}
接下来,我们需要一个辅助实体。我把它放在 旁边 package-private ,以保持它作为包的实现细节:Generated
ActualEntity
@Entity
class Generated {
@Id
@GeneratedValue(strategy = SEQUENCE, generator = "seq")
@SequenceGenerator(name = "seq", initialValue = 1, allocationSize = 1)
Long id;
}
最后,在保存 .在那里,我们创建并持久化一个实例。然后,这将提供生成的 类型为 的数据库序列。我们通过将该值写入 来利用该值。ActualEntity
Generated
id
Long
ActualEntity.generated
在我的用例中,我使用 Spring Data REST 实现了这一点,该 get 在 get 持久化之前被调用。它应该展示以下原则:@RepositoryEventHandler
ActualEntity
@Component
@RepositoryEventHandler
public class ActualEntityHandler {
@Autowired
EntityManager entityManager;
@Transactional
@HandleBeforeCreate
public void generate(ActualEntity entity) {
Generated generated = new Generated();
entityManager.persist(generated);
entity.setGlobalId(generated.getId());
entityManager.remove(generated);
}
}
我没有在实际应用程序中测试它,所以请小心享受。
我今天正在为这个问题而苦苦挣扎,能够使用它来解决
@Generated(GenerationTime.INSERT)
@Column(name = "internal_id", columnDefinition = "serial", updatable = false)
private int internalId;
评论
你可以完全按照你的要求去做。
我发现可以通过向集成商注册 Hibernate 的 IdentifierGenerator 实现来调整它们。有了这个,您应该能够使用 Hibernate 提供的任何 id 序列生成器来生成非 id 字段的序列(大概非序列 id 生成器也可以工作)。
以这种方式生成 id 有很多选项。查看 IdentifierGenerator 的一些实现,特别是 SequenceStyleGenerator 和 TableGenerator。如果您已使用 @GenericGenerator 注解配置了生成器,那么您可能熟悉这些类的参数。这也具有使用 Hibernate 生成 SQL 的优点。
以下是我如何让它工作:
import org.hibernate.Session;
import org.hibernate.boot.Metadata;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.enhanced.TableGenerator;
import org.hibernate.integrator.spi.Integrator;
import org.hibernate.internal.SessionImpl;
import org.hibernate.service.spi.SessionFactoryServiceRegistry;
import org.hibernate.tuple.ValueGenerator;
import org.hibernate.type.LongType;
import java.util.Properties;
public class SequenceIntegrator implements Integrator, ValueGenerator<Long> {
public static final String TABLE_NAME = "SEQUENCE_TABLE";
public static final String VALUE_COLUMN_NAME = "NEXT_VAL";
public static final String SEGMENT_COLUMN_NAME = "SEQUENCE_NAME";
private static SessionFactoryServiceRegistry serviceRegistry;
private static Metadata metadata;
private static IdentifierGenerator defaultGenerator;
@Override
public void integrate(Metadata metadata, SessionFactoryImplementor sessionFactoryImplementor, SessionFactoryServiceRegistry sessionFactoryServiceRegistry) {
//assigning metadata and registry to fields for use in a below example
SequenceIntegrator.metadata = metadata;
SequenceIntegrator.serviceRegistry = sessionFactoryServiceRegistry;
SequenceIntegrator.defaultGenerator = getTableGenerator(metadata, sessionFactoryServiceRegistry, "DEFAULT");
}
private TableGenerator getTableGenerator(Metadata metadata, SessionFactoryServiceRegistry sessionFactoryServiceRegistry, String segmentValue) {
TableGenerator generator = new TableGenerator();
Properties properties = new Properties();
properties.setProperty("table_name", TABLE_NAME);
properties.setProperty("value_column_name", VALUE_COLUMN_NAME);
properties.setProperty("segment_column_name", SEGMENT_COLUMN_NAME);
properties.setProperty("segment_value", segmentValue);
//any type should work if the generator supports it
generator.configure(LongType.INSTANCE, properties, sessionFactoryServiceRegistry);
//this should create the table if ddl auto update is enabled and if this function is called inside of the integrate method
generator.registerExportables(metadata.getDatabase());
return generator;
}
@Override
public Long generateValue(Session session, Object o) {
// registering additional generators with getTableGenerator will work here. inserting new sequences can be done dynamically
// example:
// TableGenerator classSpecificGenerator = getTableGenerator(metadata, serviceRegistry, o.getClass().getName());
// return (Long) classSpecificGenerator.generate((SessionImpl)session, o);
return (Long) defaultGenerator.generate((SessionImpl)session, o);
}
@Override
public void disintegrate(SessionFactoryImplementor sessionFactoryImplementor, SessionFactoryServiceRegistry sessionFactoryServiceRegistry) {
}
}
您需要在 META-INF/services 目录中注册此类。以下是 Hibernate 文档中关于注册集成商的内容:
要使集成器在 Hibernate 启动时自动使用,您需要将 META-INF/services/org.hibernate.integrator.spi.Integrator 文件添加到 jar 中。该文件应包含实现接口的类的完全限定名称。
由于此类实现 ValueGenerator 类,因此可以将其与 @GeneratorType 批注一起使用以自动生成顺序值。以下是您的类的配置方式:
@Entity
@Table(name = "MyTable")
public class MyEntity {
//...
@Id //... etc
public Long getId() {
return id;
}
@GeneratorType(type = SequenceIntegrator.class, when = GenerationTime.INSERT)
@Column(name = "SEQ_VAL", unique = false, nullable = false, insertable = true, updatable = true)
public Long getMySequencedValue(){
return myVal;
}
}
@Column(name = "<column name>", columnDefinition = "serial")
适用于 mySQL
评论
我制作了一个单独的实体表来生成,并使用它来在保存该 .
实体:id
id
id
import lombok.Data;
@Entity
@Data
public class GeneralSeqGenerator {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "my_gen")
@SequenceGenerator(name = "my_gen", sequenceName= "my_seq", allocationSize = 1, initialValue = 100000)
private long seqNumber;
}
存储 库:
public interface GeneralSeqGeneratorRepository extends JpaRepository<GeneralSeqGenerator, Long>{
}
实现持有非主服务的服务:id
...
public void saveNewEntity(...) {
...
newEntity.setNonPrimaryId(generalSeqGeneratorRepository.save(new GeneralSeqGenerator()).getSeqNumber());
...
}
...
评论
IDK 这个解决方案是对还是错,但我挖了一些代码并实现了它。它似乎工作正常。在Mysql上执行。
要求:
需要一个单独的列,而不是具有唯一值的列,并且会自动插入,并且应该在 INSERT 上插入值(而不是在 UPDATE 上)。Id
上下文:
- 假设我有一个实体名称,其代码如下:
Bug
public class Bug extends AbstractAuditingMappedEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
// want 👇 column as auto increment non id column
@Column(unique = true, nullable = false, updatable = false)
private Integer bugKey;
// ... other things
}
为了达到结果,我这样做了:
一些观察:
- 生成随机的唯一自动插入值很难生成。
- 因此,唯一一致的生成方法是使用主密钥生成策略。
Id
- 从主键值中,我们可以使用 any 来生成唯一值。
Injective Function
f(x) = y
程序:
- 制作一个自定义生成器来提取 Inserted 的值,并且我们可以使用任何 ,我用过
max
Ids
till now
f(x)
f(x) = x
;
public class BugKeyValueGenerator implements BeforeExecutionGenerator {
private IntegralDataTypeHolder previousValueHolder;
@Override
public synchronized Object generate(SharedSessionContractImplementor session, Object obj, Object idk, EventType eventType) {
String sql = String.format("select max( %s ) as id from %s",
session.getEntityPersister(obj.getClass().getName(), obj)
.getIdentifierPropertyName(),
obj.getClass().getSimpleName());
// 👇 according to your Id type.
previousValueHolder = new IdentifierGeneratorHelper.BasicHolder(Integer.class);
try {
PreparedStatement st = session.getJdbcCoordinator().getStatementPreparer().prepareStatement(sql);
try {
ResultSet rs = session.getJdbcCoordinator().getResultSetReturn().extract(st);
try {
if (rs.next()) {
previousValueHolder.initialize(rs, 0L).increment();
} else {
previousValueHolder.initialize(1L);
}
sql = null;
} finally {
session.getJdbcCoordinator().getLogicalConnection().getResourceRegistry().release(rs, st);
}
} finally {
session.getJdbcCoordinator().getLogicalConnection().getResourceRegistry().release(st);
session.getJdbcCoordinator().afterStatementExecution();
}
} catch (SQLException sqle) {
throw session.getJdbcServices().getSqlExceptionHelper().convert(
sqle,
"could not fetch initial value for increment generator",
sql
);
}
// you can use any `Injective function` 👇.
// eg
// `f(x) = x` => to get same values as Id
// `f(x) = "some string" + x` => to get values as BDK1, BDK2 ... etc...
// `f(x) = 1000+x` => to get values as 1001, 1002 ... etc...
return previousValueHolder.makeValueThenIncrement();
}
@Override
public EnumSet<EventType> getEventTypes() {
return INSERT_ONLY;
}
}
- 为此进行自定义注释。
@ValueGenerationType(generatedBy = BugKeyValueGenerator.class)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD})
public @interface BugKeyGenerator {
}
- 最后,使用此注释更新 Entity 类。
public class Bug extends AbstractAuditingMappedEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
// update 👇 column with annotation
@Column(unique = true, nullable = false, updatable = false)
@BugKeyGenerator
private Integer bugKey;
// ... other things
}
发现:
- 仅当主键为任何类型时才能使用。
number
- 可以与类型主键一起使用,当您不会任何记录表单。在这种情况下,您可以使用 sql 查询来生成新的唯一编号,而不是在 sql 查询中查找。
string
delete
max
count
- 您还可以将其用于基于收到的参数 () 的 1 的值,该值具有要插入的记录值。
auto insert
other value
obj
上一个:PostgreSQL 无缝序列
评论