【问题标题】:How do I make Spring to instantiate Custom ID generator instead of hibernate?如何让 Spring 实例化自定义 ID 生成器而不是休眠?
【发布时间】:2019-11-29 20:28:23
【问题描述】:

我正在尝试在 Spring-MVC 启动应用程序中使用自定义 ID 生成器。 但是,不是 Spring 创建 ID 生成器的实例,而是 hibernate 正在创建它,因此当我访问 ID 生成器的自动装配依赖项时,我得到了NullPointerException

如何告诉 hibernate 不要创建自定义 ID 生成器的实例并允许 Spring 这样做?

我尝试将@Component 添加到自定义ID 生成器类,但似乎hibernate 无论如何都在创建一个实例。 我也经历过Configure custom hibernate id generator in spring context,但不明白如何解决这个问题。

代码片段如下:

实体类

实体类:这是我在策略自定义 ID 生成器中提到的模型类,即要使用的 KE_IDGenerator。

@Entity
@Table(name="TAB_Error")
public class KnownErrorDto {
    @Id
    @GenericGenerator(name="seq_id", strategy="x.x.x.x.IdGenerator.KE_IDGenerator"
            )
    @GeneratedValue(generator="seq_id")
    @Column(name ="KEID")
    String KEID;
}

    

道类 道:在这个界面中,我提到了一个带有原生查询的方法,它给出下一个序列值。

public interface KnownErrorDaoInterface extends CrudRepository<KnownErrorDto,Integer> {
    @Query(value = "SELECT TAB_Err_SEQ.nextval  FROM dual", nativeQuery = 
                true)
     int getNextSeqForKnownError();
    
}
   

自定义 ID 生成器类

自定义 ID 生成器:这是从 Dao 获取序列值并返回自定义 ID 的类

public class KE_IDGenerator implements IdentifierGenerator {
    
    @Autowired
    KnownErrorDaoInterface knownErrorDaoInterface;
    
    @Override
    public Serializable generate(SharedSessionContractImplementor session, Object object)
                    throws HibernateException {
            
        String prefix = "KE";
               
        int id=knownErrorDaoInterface.getNextSeqForKnownError();
        String keId=null;
    
        keId=prefix + StringUtils.leftPad("" + id,9, '0');
                return keId;
    }
}

我遇到以下错误:

2019-07-21 20:11:11.165 ERROR 1900 --- [nio-8083-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause
    java.lang.NullPointerException: null
        at 
x.x.x.x.IdGenerator.KE_IDGenerator.generate(KE_IDGenerator.java:27) ~[classes/:na]
    

我希望 KE_IDGenerator 使用KnownErrorDaoInterface 提供的序列值生成自定义 ID。但是当我尝试访问KnownErrorDaoInterface 时,我得到了null 指针。

【问题讨论】:

    标签: hibernate spring-boot spring-mvc


    【解决方案1】:

    您的自定义 ID 生成器 KE_IDGenerator 不是 spring 管理的 bean,它将由 spring 管理,因此您不能在其中注入任何依赖项。 knownErrorDaoInterface 将始终为空。如果你想使用现有的序列表来生成 id,你可以像这样使用它

    @Entity
    @Table(name="TAB_Error")
    public class KnownErrorDto {
        @Id
        @GeneratedValue(generator="seq_name")
    @SequenceGenerator(name="seq_name",sequenceName="seq_table",allocationSize=10)
        @Column(name ="KEID")
        String KEID;
    }
    

    【讨论】:

    • 感谢您的回复@Rahil Husain。但我不想只使用序列值作为我的主键。此外,即使我将 KE_IDGenerator 标记为组件,spring 也不会创建 KE_IDGenerator 的实例
    • Spring 永远不会创建它的实例。如果您想使用任何依赖项,请使用静态帮助方法来获取 KnownErrorDaoInterface bean
    【解决方案2】:

    您可以在自定义生成器中使用generate 方法中的session 参数来执行本机语句。 这样做需要您编写自定义 SQL 语句并消除 Spring 和 Hibernate 处理它的舒适性,但它会完成这项工作。

    public class FimIdGenerator extends SequenceStyleGenerator {
        
        @Override
        public Serializable generate(SharedSessionContractImplementor session, Object object) throws HibernateException {
    
            // The object, that shall get a new ID is provided as `Object object`
            // So feel free to cast it to your own entity
            if(object instanceof FimEntity){
                FimEntity entity = (FimEntity) object;
    
                String createQuery = "CREATE SEQUENCE IF NOT EXISTS MY_CUSTOM_SEQENCE_NAME"
                session.createSQLQuery(createQuery).executeUpdate();
    
                String nextValQuery = "SELECT NEXTVAL('MY_CUSTOM_SEQENCE_NAME')";
                BigInteger newId = (BigInteger) session.createSQLQuery(nextValQuery).getSingleResult();
    
                return newId;
            }else{
                throw new HibernateException("Only entities of super type FimEntity are supported");
            }
        }
    }
    

    现在,这个例子使用了一个固定的序列名称,这不是我们都需要的,我们需要动态名称。所以我所做的是:

    在课堂上

    private final static String CREATE_TEMPLATE = "CREATE SEQUENCE IF NOT EXISTS SEQ_%03d_%s START WITH 1";
    
    private final static String NEXTVAL_TEMPLATE = "SELECT NEXTVAL('SEQ_%03d_%s');";
    

    generate 方法级别(fimId 对象是我的实体的一部分,我只是在object 上使用getFimId()

    String createQuery = String.format(CREATE_TEMPLATE, fimId.getAppiid(), fimId.getEntityType());
    session.createSQLQuery(createQuery).executeUpdate();
    
    String nextValQuery = String.format(NEXTVAL_TEMPLATE, fimId.getAppiid(), fimId.getEntityType());
    BigInteger newId = (BigInteger) session.createSQLQuery(nextValQuery).getSingleResult();
    

    这样做会在运行时创建新的序列,例如租户 16 和实体类型文档的“SEQ_001_tag”或租户 1 和实体类型标签或“SEQ_016_document”。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-09-18
      • 1970-01-01
      • 1970-01-01
      • 2020-08-16
      • 1970-01-01
      • 2014-12-05
      • 2021-10-26
      相关资源
      最近更新 更多