在 Hibernate 中绕过 GeneratedValue(合并不在数据库中的数据?)

Posted

技术标签:

【中文标题】在 Hibernate 中绕过 GeneratedValue(合并不在数据库中的数据?)【英文标题】:Bypass GeneratedValue in Hibernate (merge data not in db?) 【发布时间】:2011-03-12 19:18:05 【问题描述】:

我的问题与[1] 或[2] 中描述的相同。我需要手动设置默认自动生成的值(为什么?导入旧数据)。如[1] 中所述,使用 Hibernate 的 entity = em.merge(entity) 即可。

不幸的是,它没有。我既没有收到错误消息,也没有收到任何其他警告。该实体只是不会出现在数据库中。我正在使用 Spring 和 Hibernate EntityManager 3.5.3-Final。

有什么想法吗?

【问题讨论】:

【参考方案1】:

您需要一个正在运行的事务。

如果您的交易是手动管理的:

entityManager.getTransaction().begin();

(当然不要忘记提交)

如果您使用声明式事务,请使用适当的声明(很可能通过注释)

另外,在 log4j.properties 中将休眠日志级别设置为 debug (log4j.logger.org.hibernate=debug),以便更详细地跟踪正在发生的事情。

【讨论】:

Spring 正在使用 @Transactional 为我做这件事。 然后确保事务真的开始了。 @downvoter - 虽然这可能不是 this 情况的问题,但通常是未插入数据时的问题。所以请解释你的反对意见。 我认为这点很好,甚至认为这不是问题。【参考方案2】:

根据 Hibernate 论坛上的 Selectively disable generation of a new ID 线程,merge() 可能不是解决方案(至少不是单独的),您可能必须使用 custom generator(这是您发布的第二个链接)。

我自己没有对此进行测试,因此无法确认,但我建议阅读 Hibernate 论坛的主题。

【讨论】:

【参考方案3】:

它适用于我的项目,代码如下:

@XmlAttribute
@Id
@Basic(optional = false)
@GeneratedValue(strategy=GenerationType.IDENTITY, generator="IdOrGenerated")
@GenericGenerator(name="IdOrGenerated",
                  strategy="....UseIdOrGenerate"
)
@Column(name = "ID", nullable = false)
private Integer id;

import org.hibernate.id.IdentityGenerator;
...
public class UseIdOrGenerate extends IdentityGenerator 
private static final Logger log = Logger.getLogger(UseIdOrGenerate.class.getName());

@Override
public Serializable generate(SessionImplementor session, Object obj) throws HibernateException 
    if (obj == null) throw new HibernateException(new NullPointerException()) ;

    if ((((EntityWithId) obj).getId()) == null) 
        Serializable id = super.generate(session, obj) ;
        return id;
     else 
        return ((EntityWithId) obj).getId();

    

您基本上定义自己的 ID 生成器(基于 Identity 策略),如果未设置 ID,则将生成委托给默认生成器。

主要的缺点是它会将你绑定到 Hibernate 作为 JPA 提供者......但它与我的 mysql 项目完美配合

【讨论】:

@Jan,很有趣,与 MySQL 完全相反,SequenceGenerator 不受支持,但 IdentityGenerator 工作正常,“vive la portabilité!” (~便携性摇滚!) 我试过了,但我不断收到:“字段 'id' 没有默认值” @lior,需要使用继承自 IdentityGenerator 的 IncrementGenerator。 @MichailMichailidis 尝试删除 strategy=GenerationType.IDENTITY 在我提出一个新问题之前,我想通过向您询问 Hibernate 5 的解决方案来提供更多积分的机会,该解决方案已将 IdentityGenerator 的界面更改为 InsertGeneratedIdentifierDelegate getInsertGeneratedIdentifierDelegate(PostInsertIdentityPersister, Dialect, boolean)【参考方案4】:

对于其他想要执行此操作的人,上述操作确实很好。只是建议从对象中获取标识符而不是为每个实体类(仅用于 Id)进行继承,您可以执行以下操作:

import org.hibernate.id.IdentityGenerator;

public class UseIdOrGenerate extends IdentityGenerator 

    private static final Logger log = Logger.getLogger(UseIdOrGenerate.class
            .getName());

    @Override
    public Serializable generate(SessionImplementor session, Object object)
            throws HibernateException 
        if (object == null)
            throw new HibernateException(new NullPointerException());

        for (Field field : object.getClass().getDeclaredFields()) 
            if (field.isAnnotationPresent(Id.class)
                    && field.isAnnotationPresent(GeneratedValue.class)) 
                boolean isAccessible = field.isAccessible();
                try 
                    field.setAccessible(true);
                    Object obj = field.get(object);
                    field.setAccessible(isAccessible);
                    if (obj != null) 
                        if (Integer.class.isAssignableFrom(obj.getClass())) 
                            if (((Integer) obj) > 0) 
                                return (Serializable) obj;
                            
                        
                    
                 catch (IllegalArgumentException e) 
                    e.printStackTrace();
                 catch (IllegalAccessException e) 
                    e.printStackTrace();
                
            
        

        return super.generate(session, object);
    

【讨论】:

【参考方案5】:

另一种实现方式,更简单。

这一个适用于both基于注释或基于xml的配置:它依赖于休眠元数据来获取对象的id值。根据您的配置,将SequenceGenerator 替换为IdentityGenerator(或任何其他生成器)。 (创建装饰器而不是子类化,将装饰的 ID 生成器作为参数传递给此生成器,留给读者作为练习)。

public class UseExistingOrGenerateIdGenerator extends SequenceGenerator 
    @Override
    public Serializable generate(SessionImplementor session, Object object)
                        throws HibernateException 
        Serializable id = session.getEntityPersister(null, object)
                      .getClassMetadata().getIdentifier(object, session);
        return id != null ? id : super.generate(session, object);
    


练习答案(根据要求使用装饰器模式),未经过真正测试:

public class UseExistingOrGenerateIdGenerator implements IdentifierGenerator, Configurable 

    private IdentifierGenerator defaultGenerator;

    @Override
    public void configure(Type type, Properties params, Dialect d) 
                        throws MappingException;
        // For example: take a class name and create an instance
        this.defaultGenerator = buildGeneratorFromParams(
                params.getProperty("default"));
    

    @Override
    public Serializable generate(SessionImplementor session, Object object)
                        throws HibernateException 
        Serializable id = session.getEntityPersister(null, object)
                      .getClassMetadata().getIdentifier(object, session);
        return id != null ? id : defaultGenerator.generate(session, object);
    

【讨论】:

+1。优雅的解决方案。我可以补充一点,@GeneratedValue 没有定义任何特定的策略,在使用 Hibernate 时似乎默认为SequenceGenerator。如果我扩展 IdentityGenerator 我得到一个空 id。 如何与@GeneratedValue 一起使用? 这个解决方案效果很好,除非它涉及到 IdentityGenerator 的 JPA 2.1 模式生成。当您像上面那样创建扩展 IdentityGenerator 的自定义类时,JPA 模式生成不再将其视为标识列,因此不会生成正确的表创建脚本。虽然这可以通过在注释或休眠配置中指定列创建脚本来解决,但它不允许您改变数据库架构(例如在测试期间使用 HSQLDB)。 我在 Hibernate 的 JIRA 中添加了一个缺陷。请投票:hibernate.atlassian.net/browse/HHH-10429 感谢您的帮助,它在 5.2 中对我来说不太适用,但它的核心是黄金!我为 hibernate 5.2 更新了它:***.com/a/48819098/728602【参考方案6】:

我在这里给出了一个对我有用的解决方案: 创建自己的标识符生成器/序列生成器

public class FilterIdentifierGenerator extends IdentityGenerator implements IdentifierGenerator

@Override
public Serializable generate(SessionImplementor session, Object object)
        throws HibernateException 
    // TODO Auto-generated method stub
    Serializable id = session.getEntityPersister(null, object)
            .getClassMetadata().getIdentifier(object, session);
    return id != null ? id : super.generate(session, object);



将您的实体修改为:

@Id
@GeneratedValue(generator="myGenerator")
@GenericGenerator(name="myGenerator", strategy="package.FilterIdentifierGenerator")
@Column(unique=true, nullable=false)
private int id;
...

在保存而不是使用persist() 时使用merge()update()

【讨论】:

【参考方案7】:

更新 Laurent Grégoire 对 hibernate 5.2 的回答,因为它似乎发生了一些变化。

public class UseExistingIdOtherwiseGenerateUsingIdentity extends IdentityGenerator 

    @Override
    public Serializable generate(SharedSessionContractImplementor session, Object object) throws HibernateException 
        Serializable id = session.getEntityPersister(null, object).getClassMetadata().getIdentifier(object, session);
        return id != null ? id : super.generate(session, object);
    

并像这样使用它:(替换包名)

@Id
@GenericGenerator(name = "UseExistingIdOtherwiseGenerateUsingIdentity", strategy = "package.UseExistingIdOtherwiseGenerateUsingIdentity")
@GeneratedValue(generator = "UseExistingIdOtherwiseGenerateUsingIdentity")
@Column(unique = true, nullable = false)
protected Integer id;

【讨论】:

【参考方案8】:

如果你使用hibernate的org.hibernate.id.UUIDGenerator来生成String id我建议你使用:

public class UseIdOrGenerate extends UUIDGenerator 


    @Override
    public Serializable generate(SharedSessionContractImplementor session, Object object) throws HibernateException 
        Serializable id = session.getEntityPersister(null, object).getClassMetadata().getIdentifier(object, session);
        return id != null ? id : super.generate(session, object);
    

【讨论】:

以上是关于在 Hibernate 中绕过 GeneratedValue(合并不在数据库中的数据?)的主要内容,如果未能解决你的问题,请参考以下文章

Execption:the database returned no natively generated identity value

无法在 FROM 子句中使用子查询编写 JPQL 查询 - Spring Data Jpa - Hibernate

无法在 FROM 子句中使用子查询编写 JPQL 查询 - Spring Data Jpa - Hibernate

无法在 IntelliJ IDEA 中生成 JPA Hibernate 元模型类

Hibernate映射文件如何配置触发器

011 Hibernate反向工程构建实体和hbm文件 - bos