【问题标题】:How to use a sequence generator for a non ID field?如何为非 ID 字段使用序列生成器?
【发布时间】:2012-09-11 19:15:43
【问题描述】:
public class SequenceControlNumber extends SequenceGenerator {

    private static final Logger log =
        LoggerFactory.getLogger(SequenceGenerator.class);

    @Override
    public Serializable generate(SessionImplementor session, Object obj) {
        Connection connection = session.connection();
        try {
            PreparedStatement st = connection.prepareStatement
                   ("SELECT nextval ('sequencecontrolnumber') as nextval");
            try {
                ResultSet rs = st.executeQuery();
                try {
                    rs.next();
                    int currentVall = rs.getInt("sequencecontrolnumber");
                    int result = 0;
                    if(currentVall <255){
                        result = currentVall +1;
                    }
                    if ( log.isDebugEnabled() ) {
                        log.debug("Sequence identifier generated: " + result);
                    }
                    return result;
                }
                finally {
                    rs.close();
                }
            }
            finally {
                session.getBatcher().closeStatement(st);
            }
        }
        catch (SQLException sqle) {
            throw JDBCExceptionHelper.convert(
                    session.getFactory().getSQLExceptionConverter(),
                    sqle,"could not get next sequence value");
        }
    }

}

在我的模型类中,这是我的注释:

@GenericGenerator(name="seq_id",
        strategy="br.com.otgmobile.service.dao.SequenceControlNumber")
@GeneratedValue(generator="seq_id")
@Column(name="sequencecontrolnumber",unique=false, nullable=false)
private Integer sequenceControlNumber;

但我不断收到属性值异常。

javax.persistence.PersistenceException: org.hibernate.PropertyValueException: not-null property references a null or transient value: br.com.otgmobile.model.niagarahw06.ComandoNiagaraHw06.sequenceControlNumber
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1179)
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1112)
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1118)
at org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:618)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:240)
at $Proxy33.persist(Unknown Source)
at br.com.otgmobile.service.dao.ComandoNiagaraHw06DAO.add(ComandoNiagaraHw06DAO.java:57)
at br.com.otgmobile.service.dao.ComandoNiagaraHw06DAO$$FastClassByCGLIB$$e19cf51d.invoke(<generated>)
at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:149)
at org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.invokeJoinpoint(Cglib2AopProxy.java:689)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:90)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:622)
at br.com.otgmobile.service.dao.ComandoNiagaraHw06DAO$$EnhancerByCGLIB$$69ccb4e4.add(<generated>)
at br.com.otgmobile.server.NiagaraSocketManager.testPersistComandos(NiagaraSocketManager.java:100)
at br.com.otgmobile.server.NiagaraSocketManager.start(NiagaraSocketManager.java:45)
at br.com.otgmobile.server.NiagaraDaemon.run(NiagaraDaemon.java:25)
at java.lang.Thread.run(Thread.java:722)

Caused by: org.hibernate.PropertyValueException: not-null property references a null or transient value: br.com.otgmobile.model.niagarahw06.ComandoNiagaraHw06.sequenceControlNumber
at org.hibernate.engine.Nullability.checkNullability(Nullability.java:101)
at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:313)
at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:204)
at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:144)
at org.hibernate.ejb.event.EJB3PersistEventListener.saveWithGeneratedId(EJB3PersistEventListener.java:69)
at org.hibernate.event.def.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:179)
at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:135)
at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:61)
at org.hibernate.impl.SessionImpl.firePersist(SessionImpl.java:800)
at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:774)
at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:778)
at org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:612)

【问题讨论】:

  • 你的 HIbernate 版本是多少?

标签: java hibernate


【解决方案1】:

这至少在 Postgres 中对我有用

@Generated(GenerationTime.INSERT)
@Column(name = "internal_id", columnDefinition = "serial", updatable = false)
private int internalId;

【讨论】:

    【解决方案2】:

    对于非 id 字段的自定义序列生成器,您可以将 @GeneratorType 与 ValueGenerator 类实现一起使用。例如:

    1. 实体:
        import org.hibernate.annotations.GeneratorType
    
        @GeneratorType(type = CustomGenerator.class, when = GenerationTime.INSERT)
        @Column(name = "CUSTOM_COLUMN", unique = true, nullable = false, updatable = false, lenght = 64)
        private String custom;
    
    1. ValueGenerator 实现:
    public class CustomGenerator extends ValueGenerator<String> {
            private static final String TODAY_EXAMPLE_QUERY = "from Example where createDate>:start and createDate<:end order by createDate desc";
            private static final String START_PARAMETER = "start";
            private static final String END_PARAMETER = "end";
            private static final String NEXTVAL_QUERY = "select EXAMPLE_SEQ.nextval from dual";
            private final SimpleDateFormat dataFormat = new SimpleDateFormat("yyyyMMdd");
    
            @Override
            public String generateValue(Session session, Object owner) {
                Date now = new Date();
                Query<Example> todayQuery = session.createQuery(TODAY_EXAMPLE_QUERY, Example.class);
                query.setParameter(START_PARAMETER, start(now));
                query.setParameter(END_PARAMETER, end(now));
                Example lastExample = todayQuery.setMaxResult(1).setHibernateFlushMode(COMMIT).uniqueResult();
    
                NativeQuery nextvalQuery = session.createSQLQuery(NEXTVAL_QUERY);
                Number nextvalValue = nextvalQuery.setFlushMode(COMMIT).uniqueResult();
                return dataFormat.format(now) + someParameter(lastExample) + nextvalValue.longValue();
            }
        }
    

    【讨论】:

      【解决方案3】:

      我刚刚遇到了一个类似的问题,我需要创建一个序列并将其附加到一个不是主键的字段。

      我一直在努力尝试使用 @GeneratedValue@SequenceGenerator 注释,因为正如 Web 上所述:@GeneratedValue 只能与 Hibernate 实体字段上的 @Id 注释一起使用。 这告诉 Hibernate 不要关心该字段是否为空,它将在插入数据库时​​进行管理。但是这里 Hibernate 总是抛出异常,因为 GeneratedValue 在字段上被忽略了。

      所以解决方案很简单:只需将此注释替换为@Generated(GenerationTime.INSERT) 并将columnDefinition = "serial" 添加到您的@Column(name = "huhu", columnDefinition = "serial", updatable = false, ...)

      如果您首先创建了附加到数据库中字段的正确序列,它现在应该正常工作。

      【讨论】:

        【解决方案4】:

        检查SequenceControlNumberclass中generate方法返回的result的值。

        顺便说一句,在这个 SO 问题中检查这个回复:Hibernate JPA Sequence (non-Id)

        【讨论】:

        • 谢谢伙计,但这只是删除了异常并向我展示了没有生成序列的真正问题,因为现在我在该字段上的所有值都是 0
        • 我已经将它置于调试模式,但是当我坚持它时,调试器不会去那里,我试图查看我的注释是否有问题
        【解决方案5】:

        @GeneratedValue 注释不是 Hibernate 将作为列信息的一部分进行处理的东西。它必须与@Id 注释一起使用。它只是说当有 id 时如何生成 id。

        你有几个选项来完成你想要的,但没有一个比使用你写的注释更优雅。这些建议各有利弊(数据库可移植性、复杂性、实体管理器与会话等),但一些想法是:

        • 实现 PreInsertListener 并将其添加到您的 AnnotationConfiguration。此侦听器将查找需要此功能的实体类型并获取/分配下一个序列值
        • 创建一个数据库触发器来处理使用序列值填充列。将 Java 代码中的列标记为 insertable = false, updatable = false
        • 将生成逻辑放入实体中的回调方法中,并使用@PrePersist 注解进行标记
        • 将该字段作为构造函数的一部分进行填充(此后不首选,因为您在构造函数中有一个 DB 调用,并且存在一些潜在的事务边界歧义)

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2010-11-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-09-21
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多