web-dev-qa-db-ja.com

HibernateでGeneratedValueをバイパスします(データベースにないデータをマージしますか?)

私の問題は [1] または [2] で説明されているものと同じです。デフォルトで自動生成された値を手動で設定する必要があります(なぜ?古いデータをインポートする)。 [1] で説明されているように、Hibernateのentity = em.merge(entity)を使用するとうまくいきます。

残念ながら私にはありません。エラーもその他の警告も表示されません。エンティティはデータベースに表示されません。 SpringとHibernate EntityManager 3.5.3-Finalを使用しています。

何か案は?

37
Jan

それは私のプロジェクトで次のコードで動作します:

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

    }
}

ここでは、基本的に(Identity戦略に基づいて)独自のIDジェネレーターを定義し、IDが設定されていない場合は、生成をデフォルトジェネレーターに委任します。

主な欠点は、JPAプロバイダーとしてHibernateにバインドすることですが、MySQLプロジェクトで完全に動作します

45
Kevin

別の実装で、はるかに簡単です。

これはbothアノテーションベースまたは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);
    }
}

実際にはテストされていない、演習への回答(要求に応じてデコレータパターンを使用):

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

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;
20
Cadell Christo

Hibernateのorg.hibernate.id.UUIDGeneratorを使用して文字列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);
    }
}
6
forhas

私はここで私のために働いた解決策を与えています:
独自の識別子ジェネレータ/シーケンスジェネレータを作成する

_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()を使用

6
rakesh

Hibernateフォーラムの 新しいIDの生成を選択的に無効にする スレッドによると、merge()は(少なくとも単独では)解決策ではない可能性があり、-を使用する必要があります カスタムジェネレーター (これが2番目のリンクです)。

私はこれを自分でテストしなかったので確認できませんが、Hibernateのフォーラムのスレッドを読むことをお勧めします。

2
Pascal Thivent

これを行おうとしている他の誰にとっても、上記はうまく機能します。各Entityクラス(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);
    }
}
2
Jonathan

実行中のトランザクションが必要です。

トランザクションが手動で管理されている場合:

entityManager.getTransaction().begin();

(もちろんコミットすることを忘れないでください)

宣言型トランザクションを使用している場合は、適切な宣言を使用します(おそらくアノテーションを使用)。

また、休止状態のログレベルをdebuglog4j.logger.org.hibernate=debug)をlog4j.propertiesに追加して、発生していることを詳細に追跡します。

1
Bozho