복붙노트

[SPRING] Dozer가 최대 절전 모드 지연로드를 트리거하지 못하도록합니다.

SPRING

Dozer가 최대 절전 모드 지연로드를 트리거하지 못하도록합니다.

POJO에서 DTO로 변환 할 때 트랜잭션이 여전히 활성화되도록 Spring 트랜잭션을 사용하고 있습니다.

Dozer가 게으른로드를 유발하지 않도록 숨겨진 SQL 쿼리가 발생하지 않도록하고 싶습니다. 모든 인출은 HQL을 통해 명시 적으로 수행되어야합니다 (성능에 대한 최상의 제어권 확보).

나는 DTO 변환 전에 이것을 시도했다 :

PlatformTransactionManager tm = (PlatformTransactionManager) SingletonFactoryProvider.getSingletonFactory().getSingleton("transactionManager");
tm.commit(tm.getTransaction(new DefaultTransactionDefinition()));

나는 트랜잭션에 어떤 일이 일어나는지 모르지만, Hibernate 세션은 닫히지 않으며, 지연로드가 여전히 발생한다.

나는 이것을 시도했다 :

SessionFactory sf = (SessionFactory) SingletonFactoryProvider.getSingletonFactory().getSingleton("sessionFactory");
sf.getCurrentSession().clear();
sf.getCurrentSession().close();

그리고 게으른 로딩을 방지하지만 응용 프로그램 계층에서 직접 세션을 조작하는 것이 좋습니다 (내 프로젝트에서 "facade"라고 함)? 어떤 부작용을 두려워해야합니까? (나는 이미 POJO -> DTO 변환을 포함하는 테스트가 AbstractTransactionnalDatasource Spring 테스트 클래스를 통해 더 이상 시작할 수 없다는 것을 보았습니다. 왜냐하면이 클래스는 더 이상 활성 세션에 연결되지 않은 트랜잭션에서 롤백을 트리거하려고하기 때문입니다.)

나는 또한 전파를 NOT_SUPPORTED 또는 REQUIRES_NEW로 설정하려고 시도했지만 현재의 Hibernate 세션을 재사용하고 지연로드를 방지하지는 않습니다.

해결법

  1. ==============================

    1.내가 이것을 관리하기 위해 (사용자 정의 변환기, 이벤트 리스너 및 프록시 확인자를 살펴본 후) 찾은 유일한 일반적인 해결책은 사용자 정의 필드 매퍼를 구현하는 것입니다. Dozer API에이 기능이 숨어있는 것을 발견했습니다 (사용자 안내서에 문서화되어 있지 않다고 생각합니다).

    내가 이것을 관리하기 위해 (사용자 정의 변환기, 이벤트 리스너 및 프록시 확인자를 살펴본 후) 찾은 유일한 일반적인 해결책은 사용자 정의 필드 매퍼를 구현하는 것입니다. Dozer API에이 기능이 숨어있는 것을 발견했습니다 (사용자 안내서에 문서화되어 있지 않다고 생각합니다).

    간단한 예는 다음과 같습니다.

    public class MyCustomFieldMapper implements CustomFieldMapper 
    {
        public boolean mapField(Object source, Object destination, Object sourceFieldValue, ClassMap classMap, FieldMap fieldMapping) 
        {       
            // Check if field is a Hibernate collection proxy
            if (!(sourceFieldValue instanceof AbstractPersistentCollection)) {
                // Allow dozer to map as normal
                return false;
            }
    
            // Check if field is already initialized
            if (((PersistentSet) sourceFieldValue).wasInitialized()) {
                // Allow dozer to map as normal
                return false;
            }
    
            // Set destination to null, and tell dozer that the field is mapped
            destination = null;
            return true;
        }   
    }
    

    이것은 초기화되지 않은 PersistentSet 객체를 null로 반환합니다. 이렇게하면 클라이언트에 전달 될 때 NULL (로드되지 않은) 컬렉션과 빈 컬렉션을 구별 할 수 있습니다. 이를 통해 클라이언트에서 미리로드 된 집합을 사용하거나 집합을 검색하기 위해 다른 서비스 호출을 수행하는 일반 동작을 정의 할 수 있습니다 (필요한 경우). 또한 서비스 계층 내에서 컬렉션을 열심히로드하기로 결정하면 평소대로 매핑됩니다.

    스프링을 사용하여 사용자 정의 필드 매퍼를 주입합니다.

    <bean id="dozerMapper" class="org.dozer.DozerBeanMapper" lazy-init="false">
        <property name="mappingFiles">
            ...
        </property>
        <property name="customFieldMapper" ref="dozerCustomFieldMapper" />
    </bean>
    <bean id="dozerCustomFieldMapper" class="my.project.MyCustomFieldMapper" />
    

    인터넷을 검색 할 때 예를 찾지 못했기 때문에이 문제에 대한 해결책을 찾는 사람들에게 도움이되기를 바랍니다.

  2. ==============================

    2.위의 인기있는 버전의 변형은 PersistentBags, PersistentSets를 모두 포착합니다. 이름은 ...

    위의 인기있는 버전의 변형은 PersistentBags, PersistentSets를 모두 포착합니다. 이름은 ...

    public class LazyLoadSensitiveMapper implements CustomFieldMapper {
    
    public boolean mapField(Object source, Object destination, Object sourceFieldValue, ClassMap classMap, FieldMap fieldMapping) {
        //if field is initialized, Dozer will continue mapping
    
        // Check if field is derived from Persistent Collection
        if (!(sourceFieldValue instanceof AbstractPersistentCollection)) {
            // Allow dozer to map as normal
            return false;
        }
    
        // Check if field is already initialized
        if (((AbstractPersistentCollection) sourceFieldValue).wasInitialized()) {
            // Allow dozer to map as normal
            return false;
        }
    
        return true;
    }
    

    }

  3. ==============================

    3.나는 위의 (아마도 다른 버전) 작동하지 않았다. 그러나 이것은 잘 작동합니다.

    나는 위의 (아마도 다른 버전) 작동하지 않았다. 그러나 이것은 잘 작동합니다.

    public class HibernateInitializedFieldMapper implements CustomFieldMapper {
        public boolean mapField(Object source, Object destination, Object sourceFieldValue, ClassMap classMap, FieldMap fieldMapping) {
            //if field is initialized, Dozer will continue mapping
            return !Hibernate.isInitialized(sourceFieldValue));
        }
    }
    
  4. ==============================

    4.지연로드를 모두 비활성화하는 것을 고려 했습니까?

    지연로드를 모두 비활성화하는 것을 고려 했습니까?

    실제로 사용하고자하는 패턴을 따라하는 것은 실제로 보이지 않습니다.

    이것은 당신이 게으른 로딩을 사용하고 싶지 않다는 것을 의미합니다.

    Dozer와 당신이 그것에 전달하는 Hibernate-backed beans는 서로 행복하게 무지합니다. Dozer는 Bean의 속성에 액세스하고 있으며 Hibernate가 지원하는 Bean은 이러한 속성에 직접 액세스하는 것과 마찬가지로 게으른로드 콜렉션을 얻기위한 호출에 응답하고 있다는 것을 알고 있습니다.

    도저가 콩에서 Hibernate 프록시를 인식하도록하거나 그 반대의 경우, IMO는 앱 계층을 무너 뜨릴 것입니다.

    예상치 못한 시간에 "숨겨진 SQL 쿼리"를 실행하지 않으려면 지연로드를 비활성화하십시오.

  5. ==============================

    5.이 매퍼의 짧은 버전은

    이 매퍼의 짧은 버전은

    return sourceFieldValue instanceof AbstractPersistentCollection && 
    !( (AbstractPersistentCollection) sourceFieldValue ).wasInitialized();
    
  6. ==============================

    6.CustomFieldMapper를 사용하는 것은 소스 클래스의 모든 필드에 대해 호출 할 것이므로 좋은 생각이 아니지만, 우리의 관심은 게으른 연관 매핑 (자식 객체 목록)이므로 엔티티 객체의 getter에 null 값을 설정할 수 있습니다.

    CustomFieldMapper를 사용하는 것은 소스 클래스의 모든 필드에 대해 호출 할 것이므로 좋은 생각이 아니지만, 우리의 관심은 게으른 연관 매핑 (자식 객체 목록)이므로 엔티티 객체의 getter에 null 값을 설정할 수 있습니다.

    public Set<childObject> getChild() {
    if(Hibernate.isInitialized(child){
        return childObject;
    }else
     return null;
    }
    
  7. from https://stackoverflow.com/questions/5552379/prevent-dozer-from-triggering-hibernate-lazy-loading by cc-by-sa and MIT license