Hibernate는 왜 인자 생성자를 필요로하지 않습니까?


103

인수가없는 생성자는 요구 사항입니다 (Hibernate와 같은 도구는이 생성자에 리플렉션을 사용하여 객체를 인스턴스화합니다).

나는 손으로 물결 모양의 대답을 얻었지만 누군가가 더 설명 할 수 있습니까? 감사


7
참고 : The no-argument constructor is a requirement 틀린 주장 , 그리고 이것이 사실인지 의문을 갖지 않고 그 이유를 설명하는 모든 답변 (상금을받은 수락 된 답변 포함) 이 잘못된 것 입니다. 이 답변보기 : stackoverflow.com/a/29433238/773113
Mike Nakis 2015

2
JPA의 공급자로 최대 절전 모드를 사용하는 경우 필요합니다.
Amalgovinus

1
@MikeNakis 당신은 잘못된 마이크입니다. Hibernate는 JPA (Amalgovinus)의 공급자로 hibernate를 사용하는 경우 객체를 인스턴스화하기위한 기본 생성자가 필요합니다. 그렇지 않으면 Hibernate가 Caused by: org.hibernate.InstantiationException: No default constructor for entity: : hibernate.tutorial.Student방금 만난 경우처럼 보고 할 것입니다
Mushy

@Mushy 질문에 "hibernate"및 "orm"태그가 지정되어 있으며 "jpa"태그가 없습니다. 질문에 JPA에 대한 언급이 없습니다.
Mike Nakis

1
@MikeNakis 나는 Mike에 동의하지만 Hibernate는 "JPA"의 구현으로 사용되며 "JPA"또는 "ORM"이 없으면 사용되지 않습니다. 따라서 최대 절전 모드가 "JPA"를 구현한다고 가정합니다.
Mushy

답변:


138

Hibernate, 일반적으로 리플렉션을 통해 객체를 생성하는 코드는 Class<T>.newInstance()클래스의 새 인스턴스를 생성합니다. 이 메서드에는 개체를 인스턴스화 할 수있는 인수없는 공용 생성자가 필요합니다. 대부분의 사용 사례에서 인수가없는 생성자를 제공하는 것은 문제가되지 않습니다.

직렬화는 생성자를 호출하지 않고 객체를 생성하기 위해 jvm 매직을 사용하기 때문에 인수가없는 생성자가없는 문제를 해결할 수있는 직렬화 기반의 해킹이 있습니다. 그러나 이것은 모든 VM에서 사용할 수있는 것은 아닙니다. 예를 들어 XStream 은 인수없는 공개 생성자가없는 개체의 인스턴스를 만들 수 있지만 특정 VM에서만 사용할 수있는 소위 "향상된"모드에서 실행해야합니다. (자세한 내용은 링크를 참조하십시오.) Hibernate의 설계자는 확실히 모든 VM과의 호환성을 유지하기로 선택했기 때문에 이러한 트릭을 피하고 Class<T>.newInstance()인수가 필요없는 공식적으로 지원되는 리플렉션 방법을 사용합니다 .


31
참고 : 생성자는 공개 할 필요가 없습니다. 패키지 가시성을 가질 수 있으며 Hibernate는 setAccessible(true)그것에 있어야 합니다.
Gray

작업에 필요한 필드를 설정하기 위해 기본이 아닌 생성자로 Custom UserType을 만들 수 있습니까?
L-Samuels 2014 년

1
참조 ObjectInputStreamsun.reflect.ReflectionFactory.getReflectionFactory().newConstructorForSerialization(classToGetInstanceOf, Object.class.getConstructor()).newInstance()위해 기본 생성자 (Windows 용 JDK1.6)없이 객체를 인스턴스화하기 위해 다음과 같은 작업을 수행합니다.
SamYonnou

re : It can have package visibility and Hibernate should setAccessible(true). 않는 it반사를 통해 인스턴스 클래스의 존재를 의미? 그리고 무슨 Hibernate should setAccessible(true)뜻입니까?
Kevin Meredith

Objenesis이 작업을 수행 널리 스프링 데이터와 mockito의 같은 많은 프레임 워크에서 사용되는 github.com/easymock/objenesis
ltfishie

46

Hibernate는 개체를 인스턴스화합니다. 따라서 인스턴스화 할 수 있어야합니다. 인수가 없는 생성자가 없으면 Hibernate는 인스턴스화 하는 방법 , 즉 전달할 인수를 알지 못합니다 .

최대 절전 모드 설명서를 말한다 :

4.1.1. 인수가없는 생성자 구현

모든 퍼시 스턴트 클래스는 Hibernate가를 사용하여 인스턴스화 할 수 있도록 기본 생성자 (비공개 일 수 있음)를 가져야합니다 Constructor.newInstance(). Hibernate에서 런타임 프록시 생성을 위해 최소한 패키지 가시성을 가진 기본 생성자가있는 것이 좋습니다.


6
생성자 가시성과 관련하여 JPA v2.0을 사용하는 경우 JSR-317에 다음과 같이 표시됩니다. 인수가없는 생성자는 public 또는 protected 여야합니다 .
José Andias 2015 년

@Bozho 안녕하세요 선생님, 내부적으로 최대 절전 모드에서 Constructor.newInstance ()를 사용하여 객체를 인스턴스화하는 경우 설정자가 정의되지 않은 상태에서 어떻게 최대 절전 모드로 값을 필드에 설정합니까?
Vikas Verma

@Embeddable 비공개 하위 클래스에 대해 공개 no-arg 생성자가있는
이유를 알지 못합니다

Constructor.newInstance ()는 인수를 취하고 문제 (실제로는 문제가되지 않음)는 해당 인수를 매핑합니다. 최대 절전 모드가이 문제를 해결하지 못한 이유를 모릅니다. 비교를 위해 : Jackson의 @JsonCreator 주석이이 작업을 수행하며 불변 객체의 많은 이점이있었습니다.
drrob


43

Erm, 죄송하지만 Hibernate는 클래스가 매개 변수없는 생성자를 가져야한다고 요구 하지 않습니다 . JPA 2.0 사양 을 필요로하며, 이는 JPA를 대신하여 매우 절름발이입니다. JAXB와 같은 다른 프레임 워크도이를 필요로하며, 이러한 프레임 워크를 대신하여 매우 절망적입니다.

(사실 JAXB는 엔티티 팩토리를 허용한다고 가정하지만, 이러한 팩토리를 자체적으로 인스턴스화해야한다고 주장하며 , 내 책에서 팩토리를 허용하지 않는 것과 똑같이 좋은 매개 변수없는 생성자 를 필요로합니다 . !)

그러나 Hibernate는 그런 것을 요구하지 않습니다.

Hibernate는 인터 셉션 메커니즘 ( 문서의 "인터셉터" 참조 )을 지원하여 필요한 생성자 매개 변수로 객체를 인스턴스화 할 수 있습니다.

기본적으로 최대 절전 모드를 설정할 때 org.hibernate.Interceptor인터페이스를 구현하는 개체를 전달하면 최대 절전 모드가 instantiate()해당 개체의 새 인스턴스가 필요할 때마다 해당 인터페이스 의 메서드를 호출 하므로 해당 메서드의 구현은 new당신이 좋아하는 방식으로 당신의 물건.

나는 프로젝트에서 그것을 해왔고 그것은 매력처럼 작동합니다. 이 프로젝트에서는 가능할 때마다 JPA를 통해 작업을 수행하고 다른 옵션이 없을 때 인터셉터와 같은 Hibernate 기능 만 사용합니다.

Hibernate는 시작하는 동안 각 엔터티 클래스에 대한 정보 메시지를 발행하여 나에게 INFO: HHH000182: No default (no-argument) constructor for class및 라고 알려주기 때문에 다소 안전하지 않은 것처럼 보이지만 class must be instantiated by Interceptor나중에 인터셉터로 인스턴스화하고 만족합니다.

Hibernate 이외의 도구에 대한 질문의 "왜"부분에 답하기 위해 대답은 "절대적으로 정당한 이유가 없습니다"이며 이는 최대 절전 모드 인터셉터의 존재에 의해 입증됩니다. 클라이언트 개체 인스턴스화를위한 유사한 메커니즘을 지원할 수있는 도구가 많이 있지만 지원하지 않으므로 개체를 스스로 생성하므로 매개 변수없는 생성자가 필요합니다. 나는 이러한 도구의 제작자가 스스로를 무지한 응용 프로그램 프로그래머가 사용할 마법으로 가득 찬 프레임 워크를 만드는 닌자 시스템 프로그래머라고 생각하기 때문에 이런 일이 일어나고 있다고 믿고 있습니다. 다음과 같은 고급 구조가 필요합니다. 공장 패턴 . (괜찮아,그렇게 생각합니다. 나는 실제로 그렇게 생각 하지 않는다 . 장난이야.)


1
마침내 그것을 얻는 사람! 개체 인스턴스화 프로세스 (적절한 종속성 주입 및 풍부한 개체 동작에 절대적으로 중요 함)를 가리는 이러한 프레임 워크를 다루는 것보다 더 많은 시간을 보냈습니다. 또한 Java 리플렉션을 사용하면 newInstance ()를 사용하지 않고도 객체를 만들 수 있습니다. getDeclaredConstructors 메소드는 JDK 1.1 이후 리플렉션 API에 있습니다. JPA 사양 디자이너가이를 무시한 것은 무섭습니다.
drrob

이것은 잘못되었습니다. Hibernate가 지속성을위한 JPA 공급자로 사용되는 경우, 기본 생성자가 필요하지 않습니다. 그렇지 않으면 Caused by: org.hibernate.InstantiationException: No default constructor for entity: : hibernate.tutorial.Student최근에 발생한 다음이 javax.persistence.*;사용 되기 때문에 다음과 같은 일이 발생 org.hibernate합니다.Session, SessionFactory, and Configuration
Mushy

2
@Mushy 이것은 완벽하게 정확합니다. 왜냐하면 a) 질문은 JPA에 대한 단일 언급없이 최대 절전 모드에 관한 것이고 b) 그럼에도 불구하고 JPA에는 기본 생성자가 필요하지 않더라도 JPA가 기본 생성자를 필요로한다는 제 답변의 두 번째 문장에서 명시 적으로 언급합니다.
마이크 나 키스

36

최대 절전 모드는 필드 또는 속성 액세스 전략을 지원하는 ORM 프레임 워크입니다. 그러나 생성자 기반 매핑을 지원하지 않습니다. -같은 몇 가지 문제 때문에

클래스에 생성자가 많으면 어떻게 되나요 ?

public class Person {

    private String name;
    private Integer age;

    public Person(String name, Integer age) { ... }
    public Person(String name) { ... }
    public Person(Integer age) { ... }

}

보시다시피, Hibernate는 어떤 생성자가 호출되어야 하는지를 추측 할 수 없기 때문에 불일치 문제를 다룹니다. 예를 들어 저장된 Person 객체를 검색해야한다고 가정합니다.

Person person = (Person) session.get(Person.class, <IDENTIFIER>);

Person 객체를 검색하기 위해 Hibernate는 어떤 생성자를 호출해야합니까? 보이니?

마지막으로 리플렉션을 사용하여 Hibernate는 인수가 없는 생성자를 통해 클래스를 인스턴스화 할 수 있습니다. 그래서 전화 할 때

Person person = (Person) session.get(Person.class, <IDENTIFIER>);

Hibernate는 다음과 같이 Person 객체를 인스턴스화합니다.

Person.class.newInstance();

API 문서에 따르면

클래스는 마치 새로운 인수 목록이 표현식에

이야기의 교훈

Person.class.newInstance();

비슷하다

new Person();

다른 건


1
이것은이 질문과 관련하여 내가 찾은 가장 훌륭한 설명입니다. 내가 찾은 대부분의 답변은 책 같은 기술 용어를 사용했으며 아무도 당신처럼 유연하게 설명하지 않았습니다. 당신과 감사합니다!
The Dark Knight

1
이것은 Hibernate 팀의 추론 일 수 있습니다. 그러나 실제로 문제는 (1) 주석이 필요하거나 생성자가 하나 뿐인 경우 기본이 아닌 생성자 만 사용하고 (2) class.getDeclaredConstructors를 사용하여 해결할 수 있습니다. Class.newInstance () 대신 Constructor.newInstance ()를 사용합니다. Java 8 이전에는 XML / 주석에 적절한 매핑이 필요하지만 전적으로 가능합니다.
drrob

좋아, 그래서 최대 절전 모드는 기본 생성자에서 객체를 만든 다음 필드 nameage? 그렇지 않다면 나중에 다른 생성자를 사용합니까?
tryingHard

2
@tryingHard 예, 일단 인스턴스화되면 Hibernate는 setter 또는 필드를 사용합니다. 액세스 전략에 따라 다릅니다. 기본적으로 Id 주석의 배치는 기본 액세스 전략을 제공합니다. docs.jboss.org/hibernate/orm/5.1/userguide/html_single/chapters/…
Arthur Ronald

6

실제로, 인수가없는 생성자가없는 클래스를 인스턴스화 할 수 있습니다. 클래스 생성자의 목록을 가져 와서 하나를 선택하고 가짜 매개 변수로 호출 할 수 있습니다.

이것이 가능하고 작동하고 문제가되지 않을 것이라고 생각하지만, 꽤 이상하다는 데 동의해야합니다.

Hibernate가하는 방식으로 객체를 생성하는 것 (나는 그것이 0-arg 생성자를 호출 한 다음 아마도 Reflection을 통해 인스턴스의 필드를 직접 수정한다고 믿습니다. 아마도 setter를 호출하는 방법을 알고있을 것입니다) 객체가 어떻게 생성되어야하는지에 약간 반대합니다 Java- 새 개체가 원하는 개체가되도록 적절한 매개 변수를 사용하여 생성자를 호출합니다. 객체를 인스턴스화 한 다음 변경하는 것은 다소 "안티 자바"(또는 안티 순수 이론적 자바)라고 생각합니다. 물론 직접 필드 조작을 통해이 작업을 수행하면 캡슐화 및 모든 멋진 캡슐화 작업이 진행됩니다. .

이 작업을 수행하는 적절한 방법은 적절한 생성자를 사용하여 데이터베이스 행의 정보에서 객체를 인스턴스화하는 방법을 Hibernate 매핑에서 정의하는 것이라고 생각합니다 ... 그러나 이것은 더 복잡 할 것입니다. 더 복잡할수록 매핑이 더 복잡해지고 모두 더 "순수"합니다. 그리고 나는 이것이 현재의 접근 방식에 비해 이점이 없을 것이라고 생각합니다 ( "적절한 방식"으로 일하는 것에 대해 좋은 느낌을받는 것 외에는).

그렇게 말하고 Hibernate 접근 방식이 매우 "깨끗하지"않다는 것을 알면 0-arg 생성자를 갖는 의무가 엄격하게 필요하지는 않지만 순전히 "올바른 방식으로 수행했다고 생각하지만 요구 사항을 어느 정도 이해할 수 있습니다." "근거, 그들이"적절한 방법 "(합리적 이유이기는하지만)에서 그보다 훨씬 전에 이탈했을 때.


5

Hibernate는 (반영을 통해) 쿼리의 결과로 인스턴스를 생성해야합니다. Hibernate는이를 위해 엔티티의 인수가없는 생성자에 의존하므로 인수가없는 생성자를 제공해야합니다. 명확하지 않은 것은 무엇입니까?


어떤 조건에서 private생성자가 올바르지 않습니까? 내가보고 있어요 java.lang.InstantiationException으로도 private내 JPA 엔티티에 대한 생성자입니다. 참조 .
Kevin Meredith 2014

빈 생성자없이 (그러나 args 생성자로) 클래스를 시도했는데 작동했습니다. 최대 절전 모드에서 정보를 얻었습니다. "정보 : HHH000182 : 클래스 및 클래스에 대한 기본 (인수 없음) 생성자는 인터셉터에 의해 인스턴스화되어야합니다."그러나 예외가 없었고 개체가 DB에서 성공적으로 수신되었습니다.
lijep dam

2

리플렉션을 통해 매개 변수없는 생성자로 객체를 생성 한 다음 리플렉션을 통해 데이터로 해당 속성을 채우는 것이 훨씬 쉽습니다. 이름 / 이름 충돌, 생성자 내부의 정의되지 않은 논리를 사용하여 매개 변수화 된 생성자의 임의 매개 변수에 데이터를 일치 시키려고 시도하는 것보다 개체의 속성과 일치하지 않는 매개 변수 집합 등.

리플렉션을 통해 매개 변수화 된 생성자는 매우 취약하고 매개 변수없는 생성자는 응용 프로그램에 안정성을 제공하고 개발자에게 개체 동작에 대한 제어를 제공하기 때문에 많은 ORM과 직렬 변환기에는 매개 변수없는 생성자가 필요합니다.


나는 풍부한 도메인 객체가 될 필요가있는 것에 완전한 가변성을 강요하는 것이 여전히 더 취약하다고 주장하고 싶습니다 (엔티티가 작동하기 위해 기능이없는 데이터 백이어야하는 경우 ORM이 아닙니다. 생성자이지만 대신 풍부한 setter 호출의 정의되지 않은 순서가 있습니다.) ... 그러나 인수가있는 생성자에서 리플렉션을 수행 할 수 있음을 인정하기 때문에 +1 :)
를가 인수가있는

2

Hibernate는 지연 로딩을 위해 프록시를 사용합니다. 생성자를 정의하지 않거나 비공개로 설정하면 프록시 메커니즘에 의존하지 않는 몇 가지 작업이 계속 작동 할 수 있습니다. 예를 들어, 쿼리 API를 사용하여 객체 (생성자없이)를 직접로드합니다.

그러나 session.load method ()를 사용하는 경우 생성자의 비 가용성으로 인해 프록시 생성기 lib에서 InstantiationException이 발생합니다.

이 사람은 비슷한 상황을보고했습니다.

http://kristian-domagala.blogspot.com/2008/10/proxy-instantiation-problem-from.html


0

정적 및 비 정적 내부 클래스의 차이점을 설명하는 Java 언어 사양의이 섹션을 확인하십시오. http://java.sun.com/docs/books/jls/third_edition/html/classes.html#8.1.3

정적 내부 클래스는 개념적으로 .java 파일에 선언 된 일반 일반 클래스와 다르지 않습니다.

Hibernate는 Project 인스턴스와 독립적으로 ProjectPK를 인스턴스화해야하기 때문에 ProjectPK는 정적 내부 클래스이거나 자체 .java 파일에서 선언되어야합니다.

참조 org.hibernate.InstantiationException : 기본 생성자 없음

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.