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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3194721/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 17:17:19  来源:igfitidea点击:

Bypass GeneratedValue in Hibernate (merge data not in db?)

javahibernatespringormjpa

提问by Jan

My problem is the same as described in [1]or [2]. I need to manually set a by default auto-generated value (why? importing old data). As described in [1]using Hibernate's entity = em.merge(entity)will do the trick.

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

Unfortunately for me it does not. I neither get an error nor any other warning. The entity is just not going to appear in the database. I'm using Spring and Hibernate EntityManager 3.5.3-Final.

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

Any ideas?

有任何想法吗?

采纳答案by Kevin

it works on my project with the following code:

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

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

and

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();

    }
}

where you basically define your own ID generator (based on the Identity strategy), and if the ID is not set, you delegate the generation to the default generator.

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

The main drawback is that it bounds you to Hibernate as JPA provider ... but it works perfectly with my MySQL project

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

回答by Bozho

You need a running transaction.

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

In case your transaction are manually-managed:

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

entityManager.getTransaction().begin();

(of course don't forget to commit)

(当然不要忘记提交)

If you are using declarative transactions, use the appropriate declaration (via annotations, most likely)

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

Also, set the hibernate logging level to debug(log4j.logger.org.hibernate=debug) in your log4j.properties in order to trace what is happening in more details.

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

回答by Pascal Thivent

According to the Selectively disable generation of a new IDthread on the Hibernate forums, merge()might not be the solution (at least not alone) and you might have to use a custom generator(that's the second link you posted).

根据Hibernate 论坛上选择性禁用新 ID线程的生成merge()可能不是解决方案(至少不是唯一的),您可能必须使用自定义生成器(这是您发布的第二个链接)。

I didn't test this myself so I can't confirm but I recommend reading the thread of the Hibernate's forums.

我自己没有测试过,所以我无法确认,但我建议阅读 Hibernate 论坛的主题。

回答by Jonathan

For anyone else looking to do this, above does work nicely. Just a recommendation to getting the identifier from the object rather than having inheritance for each Entity class (Just for the Id), you could do something like:

对于其他想要这样做的人来说,上面的效果很好。只是建议从对象中获取标识符而不是为每个实体类继承(仅用于 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);
    }
}

回答by Laurent Grégtheitroade

Another implementation, way simpler.

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

This one works with bothannotation-based or xml-based configuration: it rely on hibernate meta-data to get the id value for the object. Replace SequenceGeneratorby IdentityGenerator(or any other generator) depending on your configuration. (The creation of a decorator instead of subclassing, passing the decorated ID generator as a parameter to this generator, is left as an exercise to the reader).

这个适用于基于注释的配置,也适用于基于 xml 的配置:它依赖于休眠元数据来获取对象的 id 值。根据您的配置替换SequenceGeneratorIdentityGenerator(或任何其他生成器)。(创建装饰器而不是子类化,将装饰的 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);
    }
}


Answer to the exercise (using a decorator pattern, as requested), not really tested:

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

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);
    }
}

回答by rakesh

I`m giving a solution here that worked for me:
create your own identifiergenerator/sequencegenerator

我在这里给出了一个对我有用的解决方案:
创建你自己的 identifiergenerator/sequencegenerator

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);
}

}

modify your entity as:

将您的实体修改为:

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

and while saving instead of using persist()use merge()or update()

并在保存而不是使用persist()usemerge()update()

回答by Cadell Christo

Updating Laurent Grégtheitroade's answer for hibernate 5.2 because it seems to have changed a bit.

更新 Laurent Grégtheitroade 对 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);
    }
}

and use it like this: (replace the package name)

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

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

回答by forhas

If you are using hibernate's org.hibernate.id.UUIDGeneratorto generate a String id I suggest you use:

如果您使用 hibernateorg.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);
    }
}