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

时间:2010-07-07 12:40:51

标签: java hibernate spring orm jpa

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

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

有什么想法吗?

8 个答案:

答案 0 :(得分:45)

另一种实现,更简单。

这个与基于注释或基于xml的配置一起使用:它依赖于hibernate元数据来获取对象的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 :(得分:43)

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

@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,则将生成委派给默认生成器。

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

答案 2 :(得分:12)

更新LaurentGré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;

答案 3 :(得分: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()

答案 4 :(得分:3)

如果您使用休眠的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);
    }
}

答案 5 :(得分:2)

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

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

答案 6 :(得分:2)

对于任何想要这样做的人来说,上面确实很有效。只是建议从对象获取标识符而不是每个Entity类的继承(Just for the 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);
    }
}

答案 7 :(得分:1)

您需要正在运行的交易。

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

entityManager.getTransaction().begin();

(当然不要忘记提交)

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

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