Hibernate : 모든 게으른 컬렉션을 가져 오는 모범 사례
내가 가진 것 :
@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
}
무슨 문제 :
문제는 세션이 종료 된 후에 게으른 수집을 가져올 수 없다는 것입니다. 그러나 나는 또한 진행 방법 에서 세션을 닫을 수 없습니다 .
어떤 해결책 (거친 해결책) :
a) 세션이 닫히기 전에 최대 절전 모드에서 지연 컬렉션을 가져옵니다.
entity.getAddresses().size();
entity.getPersons().size();
....
b) 아마도 더 우아한 방법은 @Fetch(FetchMode.SUBSELECT)주석 을 사용하는 것입니다.
질문:
모범 사례 / 일반적인 방법 /보다 우아한 방법은 무엇입니까? 내 개체를 JSON으로 변환하는 것을 의미합니다.
Hibernate.initialize()내 @Transactional에서 사용 하여 게으른 개체를 초기화합니다.
start Transaction
Hibernate.initialize(entity.getAddresses());
Hibernate.initialize(entity.getPersons());
end Transaction
이제 트랜잭션의 외부에서 게으른 개체를 얻을 수 있습니다.
entity.getAddresses().size();
entity.getPersons().size();
동일한 트랜잭션에서 Hibernate 개체의 Getter를 순회하여 모든 게으른 자식 개체가 다음 일반 도우미 클래스를 사용하여 열심히 가져 오도록 할 수 있습니다 .
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)이 주석으로 초기화하려는 getter에 주석을 추가하십시오.
@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();
With Hibernate 4.1.6 a new feature is introduced to handle those lazy association problems. When you enable hibernate.enable_lazy_load_no_trans property in hibernate.properties or in hibernate.cfg.xml, you will have no LazyInitializationException any more.
For More refer : https://stackoverflow.com/a/11913404/286588
It's probably not anywhere approaching a best practice, but I usually call a SIZE on the collection to load the children in the same transaction, like you have suggested. It's clean, immune to any changes in the structure of the child elements, and yields SQL with low overhead.
Try use Gson library to convert objects to Json
Example with servlets :
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);
When having to fetch multiple collections, you need to:
- JOIN FETCH one collection
- Use the
Hibernate.initializefor the remaining collections.
So, in your case, you need a first JPQL query like this one:
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);
This way, you can achieve your goal with 2 SQL queries and avoid a Cartesian Product.
if you using jpa repository, set properties.put("hibernate.enable_lazy_load_no_trans",true); to jpaPropertymap
참고URL : https://stackoverflow.com/questions/19928568/hibernate-best-practice-to-pull-all-lazy-collections
'Program Club' 카테고리의 다른 글
| iPhone 대 iPad / 브라우저의 HTML5 인라인 동영상 (0) | 2020.10.14 |
|---|---|
| Android 애플리케이션에서 YouTube 비디오를 재생하는 방법은 무엇입니까? (0) | 2020.10.14 |
| 잘못된 iPhone 응용 프로그램 바이너리 (0) | 2020.10.14 |
| 상속 : 'A'는 'B'의 액세스 할 수없는 기반입니다. (0) | 2020.10.14 |
| OpenID Connect에서 ID 토큰 만료 시간의 의도는 무엇입니까? (0) | 2020.10.14 |