2014-12-10 2 views
6

을 테스트하기위한 최대 절전 프록시를 시뮬레이션 다음과 유사한 도메인 클래스 계층 구조와 Grails를 사용하는 경우 :Grails는 :

abstract class Vehicle { ... } 
class Car extends Vehicle { ... } 
class Motorcycle extends Vehicle { ... } 

을 다음과 같은 서비스 :

class VehicleService { 
    def startRepairing(Car car) { ... } 
    def startRepairing(Motorcycle motorcycle) { ... } 
} 

우리는 매우 자주 우리가 직면 생산시 다음과 같은 오류가 발생합니다.

차량 인증 서비스의 차량 번호가인 경우인수 유형 : (Car _ $$ _ javassist_156) 값 : [Id : 42343, 클래스 : 자동차]. 가능한 해결책 : startRepairing (자동차)

우리는 우리가 프록시가 추상 클래스 Vehicle 아니라 구체적인 클래스 (Car을 구현하도록하는 모음과 같은 static hasMany = [vehicles: Vehicle],에서 Vehicle 인스턴스를 검색하기 때문에 이런 생각 Motorcycle , 등).

우리는 해결책과 방법에서 인수 유형을 제거하는 데 사용하지만, 우리는 오히려있을 것입니다 - 코드 청소기, 메서드 오버로드는 우리가 생각

하나의 솔루션은 약입니다 ... 친절한 더 IDE 가능합니다

class VehicleService { 
    def startRepairing(Vehicle vehicle) { 
     startRepairing(GrailsHibernateUtil.unwrapIfProxy(vehicle)) 
    } 
    def startRepairing(Car car) { 
     /* actual business logic here */ 
    } 
    def startRepairing(Motorcycle motorcycle) { 
     /* actual business logic here */ 
    } 
} 

그러나 문제는, 우리가 어떻게이 문제를 테스트 할 수있다 : 유형이 다른 방법과 일치하지 않을 때 악명 높은 GrailsHibernateUtil.unwrapIfProxy를 사용 하는가? 개발시 코드를 실행할 때 javassist 문제를 거의 발견하지 못하고 심지어 프로덕션 환경에서도 "임의로"(또는 더 정확하게는 우리의 지식을 벗어나는 조건으로 인해) 발생하는 것으로 보입니다.

인스턴스를 강제로 javassist 프록시로 설정할 수 있습니까? 이러한 종류의 문제에 대한 일반적인 전략은 무엇이 있습니까?

+0

당신은 groovy의 동적면을 사용하고 코드에서 Car 대신 Object를 선언 할 수 있습니다. 런타임에는 메소드가 프록시 인스턴스에서 발견되며 모든 메소드가 정상적으로 작동합니다. – MatRt

+0

테스트 제한으로 인해 내 메서드의 서명을 변경해도 좋지 않습니다. -/ – Deigote

답변

6

Hibernate는 지연로드되는 클래스 인스턴스가 필요할 때 프록시를 작성합니다. 예상되는 클래스의 인스턴스 또는 서브 클래스 인 무언가가 필요하며 완전히로드되면 기본적으로 열심히로드 된 인스턴스처럼 작동합니다. 바이트 코드 라이브러리를 사용하여 클래스의 서브 클래스를 생성하여 프록시로 사용하는 Hibernate의 접근 방식은 잘 작동합니다.

따라서 1-1의 경우, 1의 많은면에서 '게으른로드 된 인스턴스'는 프록시가됩니다. 또한 "게으른"게으른로드 된 콜렉션에서 인스턴스는 모두 프록시가됩니다. 이 기능은 일부 컬렉션의 데이터 만 필요하다는 것을 알면 더 잘 작동합니다. 요청에 따라로드해야 할 때 컬렉션을 "채우거나"쿼리는 ID 만 찾고 컬렉션의 인스턴스는 프록시가됩니다 ID 만 저장됩니다. 전체 컬렉션을 반복하면 N + 1 쿼리가 실행되지만 결국 몇 개만 필요하면 일반적으로 컬렉션이있을 때 모든 인스턴스에 대한 모든 데이터를로드하는 것보다 리소스 집약적이어야합니다. 일부만 필요한 경우 비 프록시 콜렉션 구성원 작성

프록시를 볼 수있는 다른 하나의 쉬운 장소는 load() 방법입니다. get()은 이전에로드 된 값에 대해 1 차 및 2 차 (도메인 클래스에 대해 활성화되어 있고 활성화 된 경우)를보고 즉시 데이터베이스로 이동하여 해당 ID에 대한 레코드가 없으면 null을 반환합니다. 성공했는지 알기 쉽기 때문에 예외를 throw하지 않습니다. load() 그러나 ID가 아닌 속성에 액세스하는 경우에만 데이터베이스에 도달합니다.레코드가 없으면 프록시를 생성 한 초기 load() 호출에 반드시 (시간별 또는 코드별로) 가까운 위치에있을 필요가 없기 때문에 예외가 발생하고, 지연로드로 인해 결과가 예상되므로이 경우 null이 예외입니다.

+0

Burt에 대한 설명 주셔서 감사합니다. 그것은 우리가 생각한 것입니다. 그러나 문제는 예측하기 어렵 기 때문에 테스트하기가 어렵습니다. 또한 추상 클래스 인 인스턴스로 끝나는 이유는 다른 클래스가 추상 클래스 인 컬렉션에서로드 되었기 때문입니다. – Deigote