私が持っているもの:
@Entity
public class MyEntity {
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
@JoinColumn(name = "myentiy_id")
private List<Address> addreses;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
@JoinColumn(name = "myentiy_id")
private List<Person> persons;
//....
}
public void handle() {
Session session = createNewSession();
MyEntity entity = (MyEntity) session.get(MyEntity.class, entityId);
proceed(session); // FLUSH, COMMIT, CLOSE session!
Utils.objectToJson(entity); //TROUBLES, because it can't convert to json lazy collections
}
なんという問題:
問題は、セッションが閉じられた後、遅延コレクションをプルできないことです。しかし、proceedメソッドでセッションを閉じることもできません。
What a solution(coarse solution):
a)セッションが終了する前に、休止状態を強制的に遅延コレクションをプルする
entity.getAddresses().size();
entity.getPersons().size();
....
b)おそらくもっとエレガントな方法は、@Fetch(FetchMode.SUBSELECT)
アノテーションを使用することです
質問:
それを行うためのベストプラクティス/一般的な方法/より洗練された方法は何ですか?私のオブジェクトをJSONに変換する手段です。
@Transactional
内でHibernate.initialize()
を使用して、遅延オブジェクトを初期化します。
start Transaction
Hibernate.initialize(entity.getAddresses());
Hibernate.initialize(entity.getPersons());
end Transaction
トランザクションの外側では、遅延オブジェクトを取得できます。
entity.getAddresses().size();
entity.getPersons().size();
同じトランザクションでHibernateオブジェクトのGetterを走査して、次のgenericヘルパークラスを使用して、すべての遅延子オブジェクトが確実に確実にフェッチされるようにすることができます。
HibernateUtil.initializeObject(myObject、 "my.app.model");
package my.app.util;
import Java.lang.reflect.InvocationTargetException;
import Java.lang.reflect.Method;
import Java.util.HashSet;
import Java.util.Set;
import org.aspectj.org.Eclipse.jdt.core.dom.Modifier;
import org.hibernate.Hibernate;
public class HibernateUtil {
public static byte[] hibernateCollectionPackage = "org.hibernate.collection".getBytes();
public static void initializeObject( Object o, String insidePackageName ) {
Set<Object> seenObjects = new HashSet<Object>();
initializeObject( o, seenObjects, insidePackageName.getBytes() );
seenObjects = null;
}
private static void initializeObject( Object o, Set<Object> seenObjects, byte[] insidePackageName ) {
seenObjects.add( o );
Method[] methods = o.getClass().getMethods();
for ( Method method : methods ) {
String methodName = method.getName();
// check Getters exclusively
if ( methodName.length() < 3 || !"get".equals( methodName.substring( 0, 3 ) ) )
continue;
// Getters without parameters
if ( method.getParameterTypes().length > 0 )
continue;
int modifiers = method.getModifiers();
// Getters that are public
if ( !Modifier.isPublic( modifiers ) )
continue;
// but not static
if ( Modifier.isStatic( modifiers ) )
continue;
try {
// Check result of the Getter
Object r = method.invoke( o );
if ( r == null )
continue;
// prevent cycles
if ( seenObjects.contains( r ) )
continue;
// ignore simple types, arrays und anonymous classes
if ( !isIgnoredType( r.getClass() ) && !r.getClass().isPrimitive() && !r.getClass().isArray() && !r.getClass().isAnonymousClass() ) {
// ignore classes out of the given package and out of the hibernate collection
// package
if ( !isClassInPackage( r.getClass(), insidePackageName ) && !isClassInPackage( r.getClass(), hibernateCollectionPackage ) ) {
continue;
}
// initialize child object
Hibernate.initialize( r );
// traverse over the child object
initializeObject( r, seenObjects, insidePackageName );
}
} catch ( InvocationTargetException e ) {
e.printStackTrace();
return;
} catch ( IllegalArgumentException e ) {
e.printStackTrace();
return;
} catch ( IllegalAccessException e ) {
e.printStackTrace();
return;
}
}
}
private static final Set<Class<?>> IGNORED_TYPES = getIgnoredTypes();
private static boolean isIgnoredType( Class<?> clazz ) {
return IGNORED_TYPES.contains( clazz );
}
private static Set<Class<?>> getIgnoredTypes() {
Set<Class<?>> ret = new HashSet<Class<?>>();
ret.add( Boolean.class );
ret.add( Character.class );
ret.add( Byte.class );
ret.add( Short.class );
ret.add( Integer.class );
ret.add( Long.class );
ret.add( Float.class );
ret.add( Double.class );
ret.add( Void.class );
ret.add( String.class );
ret.add( Class.class );
ret.add( Package.class );
return ret;
}
private static Boolean isClassInPackage( Class<?> clazz, byte[] insidePackageName ) {
Package p = clazz.getPackage();
if ( p == null )
return null;
byte[] packageName = p.getName().getBytes();
int lenP = packageName.length;
int lenI = insidePackageName.length;
if ( lenP < lenI )
return false;
for ( int i = 0; i < lenI; i++ ) {
if ( packageName[i] != insidePackageName[i] )
return false;
}
return true;
}
}
最善の解決策ではありませんが、私が得たものは次のとおりです。
1)この注釈で初期化するゲッターに注釈を付けます。
@Retention(RetentionPolicy.RUNTIME)
public @interface Lazy {
}
2)データベースから読み取った後、オブジェクトでこのメソッドを使用します(ジェネリッククラスに配置するか、ObjectクラスでTを変更できます)。
public <T> void forceLoadLazyCollections(T entity) {
Session session = getSession().openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
session.refresh(entity);
if (entity == null) {
throw new RuntimeException("Entity is null!");
}
for (Method m : entityClass.getMethods()) {
Lazy annotation = m.getAnnotation(Lazy.class);
if (annotation != null) {
m.setAccessible(true);
logger.debug(" method.invoke(obj, arg1, arg2,...); {} field", m.getName());
try {
Hibernate.initialize(m.invoke(entity));
}
catch (Exception e) {
logger.warn("initialization exception", e);
}
}
}
}
finally {
session.close();
}
}
Utils.objectToJson(entity);を配置します。セッションを閉じる前に呼び出します。
または、フェッチモードを設定して、次のようなコードで遊ぶこともできます
Session s = ...
DetachedCriteria dc = DetachedCriteria.forClass(MyEntity.class).add(Expression.idEq(id));
dc.setFetchMode("innerTable", FetchMode.EAGER);
Criteria c = dc.getExecutableCriteria(s);
MyEntity a = (MyEntity)c.uniqueResult();
Hibernate 4.1.6では、これらの遅延アソシエーションの問題を処理する新しい機能が導入されました。 hibernate.propertiesまたはhibernate.cfg.xmlでhibernate.enable_lazy_load_no_transプロパティを有効にすると、LazyInitializationExceptionがなくなります。
おそらくベストプラクティスに近づいているわけではありませんが、通常、コレクションでSIZE
を呼び出して、提案されているように、同じトランザクションで子をロードします。クリーンで、子要素の構造の変更に対する耐性があり、オーバーヘッドが少ないSQLを生成します。
複数のコレクションを取得する必要がある場合、次のことが必要です。
Hibernate.initialize
を使用します。したがって、あなたの場合、次のような最初のJPQLクエリが必要です。
MyEntity entity = session.createQuery("select e from MyEntity e join fetch e.addreses where e.id
= :id", MyEntity.class)
.setParameter("id", entityId)
.getSingleResult();
Hibernate.initialize(entity.persons);
これにより、2つのSQLクエリで目標を達成し、デカルト積を回避できます。
Gson
ライブラリを使用してオブジェクトをJsonに変換してみてください
サーブレットの例:
List<Party> parties = bean.getPartiesByIncidentId(incidentId);
String json = "";
try {
json = new Gson().toJson(parties);
} catch (Exception ex) {
ex.printStackTrace();
}
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.getWriter().write(json);
jpaリポジトリを使用する場合、properties.put( "hibernate.enable_lazy_load_no_trans"、true);を設定します。 jpaPropertymapへ