Por que o Hibernate não requer nenhum construtor de argumentos?

O construtor sem argumentos é um requisito (ferramentas como o Hibernate usam a reflection sobre este construtor para instanciar objects).

Eu tenho essa resposta, mas alguém poderia explicar melhor? obrigado

Hibernate, e código em geral que cria objects através da reflection, usam Class.newInstance() para criar uma nova instância de suas classs. Esse método requer um construtor público no-arg para poder instanciar o object. Para a maioria dos casos de uso, fornecer um construtor no-arg não é um problema.

Há hacks baseados em serialização que podem trabalhar em torno de não ter um construtor no-arg, já que a serialização usa o jvm magic para criar objects sem invocar o construtor. Mas isso não está disponível em todas as VMs. Por exemplo, XStream pode criar instâncias de objects que não têm um construtor público no-arg, mas apenas executando em um modo chamado “aprimorado” que está disponível somente em determinadas VMs. (Veja o link para detalhes.) Os designers do Hibernate certamente escolheram manter a compatibilidade com todas as VMs e assim evitam tais truques, e usa o método de reflection oficialmente suportado Class.newInstance() exigindo um construtor no-arg.

O Hibernate instancia seus objects. Por isso, precisa ser capaz de instanciá-los. Se não houver um construtor no-arg, o Hibernate não saberá como instanciá-lo, ou seja, qual argumento deve ser passado.

A documentação do hibernate diz:

4.1.1. Implementar um construtor sem argumento

Todas as classs persistentes devem ter um construtor padrão (que pode ser não público) para que o Hibernate possa instanciá-las usando Constructor.newInstance() . É recomendável que você tenha um construtor padrão com pelo menos visibilidade de pacote para geração de proxy de tempo de execução no Hibernate.

O hibernate é uma estrutura ORM que suporta a estratégia de access de campo ou propriedade. No entanto, ele não suporta mapeamento baseado em construtor – talvez o que você gostaria? – por causa de alguns problemas como

O que acontece se sua class contém muitos construtores

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

Como você pode ver, você lida com um problema de inconsistência porque o Hibernate não pode supor qual construtor deve ser chamado. Por exemplo, suponha que você precise recuperar um object Person armazenado

 Person person = (Person) session.get(Person.class, ); 

Qual construtor deve o Hibernate chamar para recuperar um object Person? Você pode ver ?

E finalmente, usando a reflection, o Hibernate pode instanciar uma class através de seu construtor no-arg. Então quando você ligar

 Person person = (Person) session.get(Person.class, ); 

O Hibernate irá instanciar seu object Person da seguinte forma

 Person.class.newInstance(); 

Que de acordo com a documentação da API

A class é instanciada como se por uma nova expressão com uma lista de argumentos vazia

Moral da história

 Person.class.newInstance(); 

é similar a

 new Person(); 

Nada mais

Desculpa todo mundo, mas o Hibernate não exige que suas classs tenham um construtor sem parâmetros. A especificação JPA 2.0 exige isso, e isso é muito ruim em nome da JPA. Outros frameworks como o JAXB também o requerem, o que também é muito fraco em nome desses frameworks.

(Na verdade, o JAXB supostamente permite fábricas de entidade, mas insiste em instanciar essas fábricas por si só, exigindo que elas tenham um –guess what – construtor sem parâmetros , que em meu livro é exatamente tão bom quanto não permitir fábricas; !)

Mas o Hibernate não exige tal coisa.

O Hibernate suporta um mecanismo de interceptação, (veja “Interceptor” na documentação ), que permite instanciar seus objects com qualquer parâmetro de construtor que eles precisem.

Basicamente, o que você faz é que quando você configura o hibernate você passa a ele um object implementando a interface org.hibernate.Interceptor , e o hibernate então invocará o método instantiate() dessa interface sempre que precisar de uma nova instância de um object seu , então sua implementação desse método pode criar new objects da maneira que você preferir.

Eu fiz isso em um projeto e funciona como um encanto. Neste projeto eu faço coisas via JPA sempre que possível, e eu só uso resources do Hibernate como o interceptador quando não tenho outra opção.

Hibernate parece ser um pouco inseguro sobre isso, pois durante a boot ele emite uma mensagem de informação para cada uma das minhas classs de entidade, me informando INFO: HHH000182: No default (no-argument) constructor for class e class must be instantiated by Interceptor , mas então mais tarde eu os instancio por interceptor, e fica feliz com isso.

Para responder a parte “por que” da pergunta para outras ferramentas além do Hibernate , a resposta é “absolutamente sem uma boa razão”, e isso é provado pela existência do interceptador de hibernação. Existem muitas ferramentas por aí que poderiam ter suportado algum mecanismo similar para instanciação de objects cliente, mas não o fazem, então eles criam os objects sozinhos, então eles precisam exigir construtores sem parâmetros. Estou tentado a acreditar que isso está acontecendo porque os criadores dessas ferramentas pensam em si mesmos como programadores de sistemas ninja que criam frameworks cheios de magia para serem usados ​​por programadores de aplicativos ignorantes, que (para eles pensam) nunca teriam em seus sonhos necessidade de tais construções avançadas como o … Padrão de fábrica . (Ok, eu estou tentado a pensar assim. Eu realmente não penso assim. Estou brincando.)

Na verdade, você pode instanciar classs que não possuem um construtor 0-args; você pode obter uma lista de construtores de class, escolher um e invocá-lo com parâmetros falsos.

Embora isso seja possível, e eu acho que funcionaria e não seria problemático, você terá que concordar que isso é bem estranho.

Construindo objects da maneira que o Hibernate faz (eu acredito que invoca o construtor 0-arg e então ele provavelmente modifica os campos da instância diretamente via Reflection. Talvez ele saiba como chamar setters) vai um pouco contra como um object é construído em Java- invoca o construtor com os parâmetros apropriados para que o novo object seja o object desejado. Acredito que instanciar um object e depois transformá-lo é um pouco “anti-Java” (ou, eu diria anti Java puro teórico) – e definitivamente, se você fizer isso por manipulação direta de campo, ele encapsula e todo aquele material de encapsulamento .

Eu acho que a maneira correta de fazer isso seria definir no mapeamento do Hibernate como um object deve ser instanciado a partir das informações na linha do database usando o construtor adequado … mas isso seria mais complexo – o que significa que o Hibernate seria ainda mais mais complexo, o mapeamento seria mais complexo … e tudo mais “puro”; e eu não acho que isso teria uma vantagem sobre a abordagem atual (além de se sentir bem em fazer as coisas “da maneira correta”).

Dito isto, e vendo que a abordagem do Hibernate não é muito “limpa”, a obrigação de ter um construtor de 0-arg não é estritamente necessária, mas eu posso entender um pouco o requisito, embora eu acredite que eles o fizeram de maneira puramente “correta”. “motivos, quando eles se afastaram da” maneira correta “(embora por razões razoáveis) muito antes disso.

O Hibernate precisa criar instâncias como resultado de suas consultas (via reflection), o Hibernate depende do construtor no-arg de entidades para isso, então você precisa fornecer um construtor no-arg. O que não está claro?

É muito mais fácil criar um object com um construtor sem parâmetros através da reflection e, em seguida, preencher suas propriedades com dados por meio da reflection, do que tentar combinar dados com parâmetros arbitrários de um construtor parametrizado, alterando nomes / conflitos de nomes, lógica indefinida dentro do construtor, conjuntos de parâmetros não correspondentes propriedades de um object, et cetera.

Muitos ORMs e serializadores requerem construtores sem parâmetros, porque os construtores parametrizados através da reflection são muito frágeis, e os construtores sem parâmetros fornecem tanto estabilidade à aplicação quanto controle sobre o comportamento do object para o desenvolvedor.

O Hibernate usa proxies para carregamento lento. Se você não definir um construtor ou torná-lo privado, algumas coisas ainda podem funcionar – aquelas que não dependem do mecanismo de proxy. Por exemplo, carregar o object (sem construtor) diretamente usando a API de consulta.

Mas, se você usar o método session.load (), você enfrentará InstantiationException a partir do gerador de proxy lib devido à indisponibilidade do construtor.

Esse cara relatou uma situação semelhante:

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

Confira esta seção da especificação da linguagem Java que explica a diferença entre classs internas estáticas e não estáticas: http://java.sun.com/docs/books/jls/third_edition/html/classs.html#8.1.3

Uma class interna estática não é conceitualmente diferente de uma class geral regular declarada em um arquivo .java.

Como o Hibernate precisa instanciar o ProjectPK independentemente da instância do Project, o ProjectPK precisa ser uma class interna estática ou declarado em seu próprio arquivo .java.

referência org.hibernate.InstantiationException: Nenhum construtor padrão