簡體   English   中英

在Hibernate中繞過GeneratedValue(合並數據不在db?中)

[英]Bypass GeneratedValue in Hibernate (merge data not in db?)

我的問題與[1][2]中描述的相同。 我需要手動設置默認的自動生成值( 為什么?導入舊數據 )。 [1]中所述,使用Hibernate的entity = em.merge(entity)就可以了。

不幸的是,它沒有。 我既沒有得到錯誤也沒有任何其他警告。 該實體不會出現在數據庫中 我正在使用Spring和Hibernate EntityManager 3.5.3-Final。

有任何想法嗎?

另一種實現方式更簡單。

這一次工作基於XML注釋或基於配置:它依靠休眠元數據中獲取價值為對象的ID。 IdentityGenerator (或任何其他生成器)替換SequenceGenerator ,具體取決於您的配置。 (創建裝飾器而不是子類化,將裝飾的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);
    }
}

它適用於我的項目,代碼如下:

@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項目完美配合

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

我在這里給出了一個對我有用的解決方案:
創建自己的識別器/序列生成器

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

如果你使用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論壇上選擇性地禁用新ID線程的生成merge()可能不是解決方案(至少不是單獨的),您可能必須使用自定義生成器 (這是您發布的第二個鏈接)。

我沒有自己測試,所以我無法確認,但我建議閱讀Hibernate論壇的主題。

對於任何想要這樣做的人來說,上面確實很有效。 只是建議從對象獲取標識符而不是為每個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);
    }
}

您需要一個正在運行的事務

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

entityManager.getTransaction().begin();

(當然不要忘記提交)

如果您正在使用聲明性事務,請使用適當的聲明(通過注釋,最有可能)

另外,在log4j.properties中將hibernate日志記錄級別設置為debuglog4j.logger.org.hibernate=debug ),以便更詳細地跟蹤發生的情況。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM