Este artigo descreve a tecnologia de carregamento de latência do hibernato. Compartilhe -o para sua referência, como segue:
O carregamento preguiçoso de Hibernae é uma técnica muito comum. Os atributos da coleção de entidades serão adiados por padrão, e as entidades associadas às entidades também serão adiadas por padrão. O Hibernate usa esse carregamento atrasado para reduzir a sobrecarga de memória do sistema, garantindo assim o desempenho operacional do hibernado.
Vamos primeiro analisar o "segredo" do carregamento de atraso de hibernato.
Carregamento preguiçoso das propriedades de coleta
Quando o hibernato inicializa uma entidade persistente do banco de dados, o atributo de coleção dessa entidade é inicializado com a classe persistente? Se o atributo de coleção contiver 100.000 ou até milhões de registros, o rastreamento de todos os atributos de coleta ao inicializar a entidade persistente resultará em um declínio acentuado no desempenho. É inteiramente possível que o sistema só precise usar alguns registros nos atributos de coleta da classe persistente, e nem todos os atributos da coleção. Dessa forma, não há necessidade de carregar todos os atributos de coleta de uma só vez.
As estratégias de carregamento preguiçosas são geralmente recomendadas para propriedades de coleta. O chamado carregamento atrasado é carregar dados associados do banco de dados quando o sistema precisa usar os atributos de coleta.
Por exemplo, a classe de pessoa a seguir possui um atributo de coleção, e o elemento no atributo de coleção possui o endereço de tipo, e o trecho de código da classe Pessoa é o seguinte:
Listagem 1. Person.java
public class Pessoa {// Identifique o atributo ID inteiro privado; // Nome da pessoa atributo Nome da string privada; // Mantenha a idade da pessoa atributo private Int Age; // Use definido para salvar o atributo de coleção Private Set <endereço> endereços = novo hashset <deeddle> (); // Os métodos Setter e Getter de cada atributo são omitidos abaixo ...}Para que o Hibernate gerencie as propriedades de coleta da classe persistente, o programa fornece os seguintes arquivos de mapeamento para a classe persistente:
Listagem 2. Person.hbm.xml
<? xml versão = "1.0" coding = "gbk"?> <! package = "org.crazyit.app.domain"> <!-mapeando a classe de persistência da pessoa-> <classe name = "pessoa" tabela = "pessoa_inf"> <!-mapeamento de identificação de identificação ID-> <id name = "id" column = "person_id"> <!-define Primary Gerator Policy-> <gerator/> <! Nome = "AGE" TIPO = "INT"/> <!-Atributos da coleção de mapas-> <set name = "endereços" tabela = "Person_address" lazy = "true"> <!-Especifique a coluna de chave estrangeira associada-> <key column = "Person_id"/> <Composite-ELEMENT> <!-!-! name = "zip"/> </low-elelement> </set> </s class> </hibernate-mapping>
A partir do código acima que mapeia o arquivo, podemos ver que a classe de endereço do atributo de coleção da pessoa é apenas um pojo normal. A classe de endereço contém dois atributos: detalhes e zip. Como o código da classe de endereço é muito simples, o código para esta classe não é mais fornecido aqui.
O código no elemento <set .../> no arquivo de mapeamento acima especifica LAZY = "true" (para <set .../> elemento, preguiçoso = "true" é o valor padrão), que especifica que o hibernato atrasará o carregamento do objeto de endereço no atributo de coleção.
Por exemplo, carregue uma entidade pessoa com ID 1 seguindo o seguinte código:
Sessão session = sf.getCurrentSession (); transação tx = session.begIntransaction (); pessoa p = (pessoa) session.get (Person.class, 1); // <1> System.out.println (p.getName ());
O código acima só precisa acessar a entidade pessoa com ID 1 e não deseja acessar o objeto de endereço associado a essa entidade pessoa. Existem duas situações neste momento:
1. Se o carregamento não estiver atrasado, o Hibernate pegará imediatamente o objeto de endereço associado à entidade da pessoa ao carregar o registro de dados correspondente à entidade da pessoa.
2. Se o carregamento preguiçoso for usado, o Hibernate carregará apenas os registros de dados correspondentes à entidade da pessoa.
É óbvio que a segunda abordagem não apenas reduz a interação com o banco de dados, mas também evita a sobrecarga de memória causada por entidades de endereço de carregamento - é também por isso que o hibernato permite o carregamento preguiçoso por padrão.
A questão agora é: como o carregamento preguiçoso é implementado? Hibernate Qual é o valor da propriedade aborda da entidade pessoal ao carregar uma entidade pessoa?
Para resolver esse problema, definimos um ponto de interrupção no código <1> e depra -o no eclipse. Neste momento, podemos ver que a janela do console do Eclipse possui a saída, como mostrado na Figura 1:
Figura 1. Saída do console para propriedades de coleta de carregamento preguiçoso
Conforme mostrado na saída na Figura 1, o Hibernate apenas obtém dados da tabela de dados correspondentes à entidade da pessoa e não obtém dados da tabela de dados correspondente ao objeto de endereço. Isso é um carregamento preguiçoso.
Então, qual é a propriedade da entidade da pessoa? Neste momento, você pode ver os resultados mostrados na Figura 2 da janela Variáveis do Eclipse:
Figura 2.
A partir do conteúdo da caixa da Figura 2, pode -se observar que a propriedade Endereços não é as classes familiares de implementação como Hashset e TreeSet, mas uma classe de implementação do PersistEntSet, que é uma classe de implementação fornecida pelo Hibernate para a interface definida.
O objeto PersistEntSet Collection não captura os dados da tabela de dados subjacentes, por isso é naturalmente impossível inicializar o objeto de endereço na coleção. No entanto, a coleção PersistEntSet possui um atributo de sessão, que é a sessão de hibernato. Quando o programa precisar acessar o elemento de coleta PersistentSet, o PersistEntSet usará este atributo de sessão para obter os registros de dados correspondentes ao objeto de endereço real.
Então, o que exatamente você pega os registros de dados correspondentes a essas entidades de endereço? Isso não é difícil para o PersistEntSet, porque também há um atributo do proprietário na coleção PersistentSet, que indica a entidade pessoa à qual o objeto de endereço pertence. O Hibernate procurará os dados da tabela de dados correspondentes ao endereço correspondente à tabela de dados.
Por exemplo, clicamos na linha de endereços na janela mostrada na Figura 2, o que significa que dizemos ao Eclipse para depurar e produzir o atributo Endereços. Isso é para acessar o atributo endereços. No momento, você pode ver as seguintes instruções SQL na janela do console do Eclipse:
Selecione endereços0_.person_id como pessoa1_0_0_, endereços0_.detail como detalhes0_, endereços0_.zip como zip0_from Person_address endereços0_where endereços0_.person_id =?
Esta é a coleção PersistEntSet e as instruções SQL que capturam registros de endereço específicos de acordo com o atributo do proprietário. No momento, você pode ver a saída mostrada na Figura 3 da janela Variáveis do Eclipse:
Figura 3. Valores de atributo de coleta carregados
Como pode ser visto na Figura 3, o atributo Endereços no momento foi inicializado e o conjunto contém 2 objetos de endereço, que são os dois objetos de endereço associados à entidade da pessoa.
A partir da introdução acima, podemos ver que a chave para atrasar o carregamento dos atributos definidos do hibernato está na classe de implementação do PersistentSet. Durante o carregamento preguiçoso, a coleção PersistentSet não possui nenhum elemento. No entanto, o PersistEntSet manterá uma sessão de hibernato, que pode garantir que, quando o programa precisar acessar a coleção, o registro de dados seja carregado "imediatamente" e carregue os elementos de coleta.
Semelhante à classe de implementação de PersistEntSet, o Hibernate também fornece lista persistente, persistente, persistente e classes persistentes e outras classes de implementação, e suas funções são aproximadamente semelhantes às do PersistEntSet.
Os leitores familiarizados com os atributos de coleta de hibernados devem se lembrar: o hibernato exige que os atributos de declaração de coleta só possam ser usados com interfaces como set, listar, mapa, streoudset, classedmap etc., e não podem ser implementados usando classes de hashset, Arraylist, hashmap, treeset, tendap e outras implementação. The reason is that Hibernate needs to delay loading the collection attributes, and the delay loading of Hibernate relies on PersistentSet, PersistentList, PersistentMap, PersistentSortedMap, and PersistentSortedSet to complete - that is, the underlying Hibernate needs to use its own collection implementation class to complete the lazy loading, so it requires developers to use the collection interface, rather than the collection implementation class to declare collection atributos.
O Hibernate usa o carregamento preguiçoso para atributos de coleta por padrão. Em alguns casos especiais, defina o atributo lazy = "false" para elementos como <set .../>, <list .../>, <map .../> para cancelar o carregamento preguiçoso.
Atraso no carregamento de entidades associadas
Por padrão, o Hibernate também usará o carregamento preguiçoso para carregar a entidade associada. Seja uma associação individual, uma associação individual ou uma associação de muitos para muitos, o Hibernate usará o carregamento preguiçoso por padrão.
Para entidades associadas, elas podem ser divididas em dois casos:
1. Quando uma entidade associada é várias entidades (incluindo muitos, muitos para muitos): neste momento, a entidade associada existirá na forma de uma coleção, e o hibernato usará persistentes, persistentes e persistentes e persistentes, persistindo map, persistentes e outras coleções para gerenciar entidades de carga preguiçosa. Esta é a situação introduzida anteriormente.
2. Quando uma entidade associada é uma entidade única (incluindo um a um e muitos para um): quando o hibernato carrega uma entidade, a entidade associada atrasada será um objeto proxy gerado dinamicamente.
Quando a entidade associada é uma única entidade, ou seja, quando a entidade associada é mapeada usando <Many-to-One .../> ou <One-One .../>, esses dois elementos também podem especificar o carregamento preguiçoso através do atributo preguiçoso.
O exemplo a seguir também mapeia a classe de endereço para uma classe persistente. No momento, a classe de endereço também se torna uma classe de entidade, e a entidade pessoal e a entidade de endereço formam uma associação de mão dupla. O código do arquivo de mapeamento no momento é o seguinte:
Listagem 3. Person.hbm.xml
<? xml versão = "1.0" coding = "gbk"?> <!-Especifique as informações do DTD para hibernate-> <! 3.0 // pt "" http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd "> <hibernate-mapping package =" org.crazyit.app.apd.domain "> <!-mapeando a classe de persistência-> <classe <nome da classe =" tabela ") <! Column = "Person_id"> <!-Definir Política de gerador de chaves primárias-> <gerator/> <!-Usado para mapear atributos comuns-> <propriedade name = "name" type = "string"/> <names name = "idade" tipo "int"/> <!-MAP Coleção atributos, o elemento de coleção é outro que a entidade persistente não é especificada-> <! Inverse = "true"> <!-Especifique a coluna de chave estranha associada-> <key column = "Person_id"/> <!-Usada para mapear para atributos de classe associados-> <One-to-many/> </Set> </s class> <!-Mapa identificação de classificação persistente-> <class = "endereço" tabela = "endereço) <!-Map Map Map column="address_id"><!-- Specify primary key generator policy--><generator/></id><!-- Map Normal attribute detail --><property name="detail"/><!-- Map Normal attribute zip --><property name="zip"/><!-- The column name must be specified as person_id, which is the same as the column attribute value of the key element in the associated entity--><many-to-one name = "Person" column = "Person_id" não-null = "true"/> </class> </hibernate-mapping>
Em seguida, o programa carrega a entidade Person com ID 1 através do seguinte snippet de código:
// Abra a sessão de sessão dependente do contexto = sf.getCurrentSession (); transação tx = session.begIntransaction (); endereço de endereço = (endereço) session.get (endereço.class, 1); // <1> System.out.println (endereço.getDetail ());
Para ver o processamento da entidade associada do Hibernate ao carregar a entidade de endereço, definimos um ponto de interrupção no código <1> e depra -o no Eclipse. Neste momento, podemos ver que a janela do console do Eclipse produz a seguinte instrução SQL:
Selecione endereço0_.address_id como endereço1_1_0_, endereço0_.detail como detalhe1_0_, endereço0_.zip como zip1_0_, endereço0_.person_id como pessoa4_1_0_ do endereço_Inf ADDERET0_SEDED0_.Address_id =?
Não é difícil ver nesta declaração SQL que o hibernato carrega a tabela de dados correspondente à entidade de endereço para rastejar registros, mas não rasteja registros da tabela de dados correspondentes à entidade pessoal, que é que o carregamento preguiçoso desempenha uma função.
Na janela Variáveis do Eclipse, consulte a saída mostrada na Figura 4:
Figura 4. Entidade de carregamento atrasada
Pode ser visto claramente na Figura 4 que a entidade pessoal associada à entidade de endereço não é um objeto de pessoa, mas uma instância da pessoa _ $$ _ Javassist_0 classe. Esta classe é uma classe proxy gerada dinamicamente pelo Hibernate usando o projeto Javassist. Quando os atrasos no hibernato carregando a entidade associada, o Javassist será usado para gerar um objeto dinâmico de proxy, e esse objeto proxy será responsável por proxer o "não carregado ainda".
Enquanto o aplicativo precisar usar uma entidade associada que "ainda não seja carregada", a pessoa _ $$ _ Javassist_0 Objeto Proxy será responsável por carregar a entidade associada real e retornar a entidade associada real - esse é o padrão de proxy mais típico.
Clique no atributo da pessoa na janela Variáveis mostrada na Figura 4 (ou seja, forçar o atributo de pessoa a ser usado no modo de depuração) e, em seguida, você verá as seguintes instruções SQL Said na janela do console do eclipse:
Selecione PERSON0_.PERSON_ID AS PERSON1_0_0_, PERSON0_.
A instrução SQL acima é uma declaração que captura a entidade associada de "carregamento de atraso". No momento, você pode ver os resultados mostrados na Figura 5 da saída da janela Variáveis:
Figura 5. Entidade carregada
O Hibernate adota o modo de "carga atrasada" para gerenciar entidades associadas. De fato, ao carregar a entidade principal, ela realmente não pega os dados correspondentes da entidade associada, mas apenas gera dinamicamente um objeto como o proxy da entidade associada. Quando um aplicativo realmente precisa usar uma entidade associada, o objeto proxy é responsável por pegar registros do banco de dados subjacente e inicializar a entidade associada real.
No carregamento de atraso do hibernato, o que o programa do cliente começa a obter é um objeto proxy gerado dinamicamente, enquanto a entidade real é delegada ao objeto Proxy para gerenciamento - esse é o padrão de proxy típico.
Modo de agente
O modo proxy é um modo de design com um aplicativo muito amplo. Quando o código do cliente precisa chamar um objeto, o cliente realmente não se importa se deve obter o objeto com precisão. Ele precisa apenas de um objeto que possa fornecer a função. Neste momento, podemos retornar o proxy (proxy) do objeto.
Neste método de design, o sistema fornecerá a um objeto um objeto proxy e o objeto proxy controla a referência ao objeto de origem. Um proxy é um objeto Java que atua em nome de outro objeto Java. Em alguns casos, o código do cliente não deseja ou não pode chamar diretamente o Callee, e o objeto proxy pode atuar como um intermediário entre o cliente e o objeto de destino.
Para os clientes, ele não pode distinguir a diferença entre um objeto proxy e um objeto real, nem precisa distinguir a diferença entre um objeto proxy e um objeto real. O código do cliente não conhece o objeto de proxy real. O código do cliente é orientado para a interface e mantém apenas uma interface do objeto proxy.
Em resumo, desde que o código do cliente não possa ou não desejar acessar diretamente o objeto chamado - há muitas razões para essa situação, como criar um objeto com uma alta sobrecarga do sistema, ou o objeto chamado está em um host remoto, ou a função do objeto de destino não é suficiente para atender às necessidades ..., mas um objeto adicional é criado para retornar ao cliente para uso, de modo que esse método de design é o modo de prox.
O seguinte demonstra um modo de proxy simples. O programa primeiro fornece uma interface de imagem, representando a interface implementada por um grande objeto de imagem. O código da interface é o seguinte:
Listagem 3. Image.java
Imagem de interface pública {void show ();}Essa interface fornece uma classe de implementação que simula um objeto de imagem grande e o construtor da classe de implementação usa o método Thread.sleep () para pausar 3s. Abaixo está o código do programa para o BigImage.
Listagem 4. BigImage.java
// Use este bigImage para simular uma grande classe public classe bigimage implementa a imagem {public bigImage () {try {// Programa pausa 3s Sistema de simulação de modo de modo thread.sleep (3000); System.out.println ("Carregando de imagem com sucesso ...");} catch (interruptedException ex) {Ex.PrintStackTrace ();}} // Implemente o método show () na imagem public void show () {System.out.println ("Desenhe a imagem grande real);O código do programa acima faz uma pausa em 3s, o que indica que leva o tempo superior do 3s para criar um objeto BigImage - o programa usa esse atraso para simular a sobrecarga do sistema causada pelo carregamento dessa imagem. Se o modo proxy não for usado, o sistema gerará um atraso 3S quando o BigImage for criado no programa. Para evitar esse atraso, o programa fornece um objeto de proxy para o objeto Bigimage, e a classe de proxy da classe Bigimage é a seguinte.
Listagem 5. ImageProxy.java
classe pública ImageProxy implementa a imagem {// combina uma instância de imagem como o objeto proxy imagem privada imagem; // use entidades abstratas para inicializar o objeto proxy public imageproxy (imagem de imagem) {this.image = imagem;}/*** reescrever o método Show () da imagem* Este é o método e o método é usado para controlar o acessório do proxy,* show () {// crie o objeto proxy somente se (image == null) {image = new bigImage ();} image.show ();}}A classe ImageProxy Proxy acima implementa o mesmo método Show () que o BigImage, que permite que o código do cliente use o objeto Proxy como BigImage após a obtenção do objeto Proxy.
A lógica de controle é adicionada ao método show () da classe ImageProxy. Essa lógica de controle é usada para controlar que o objeto Bigimage proxy será criado apenas quando o sistema realmente chamar o show () da imagem. O programa a seguir precisa usar o objeto Bigimage, mas o programa não retorna diretamente a instância do BigImage, mas primeiro retorna o objeto BigImage Proxy, conforme mostrado no programa a seguir.
Listagem 6. bigImageTest.java
classe pública BigImageTest {public static void main (string [] args) {long start = system.currenttimEmillis (); // o programa retorna um objeto de imagem, que é apenas o objeto proxy da imagem bigimage = new imageProxy (null); system.out.println ("a tendência do tempo do sistema obtenha O programa realmente criará o objeto proxy quando o método show () do proxy da imagem for realmente chamado. Image.Show ();}}O programa acima inicializa a imagem muito rapidamente porque o programa não cria o objeto Bigimage, mas apenas obtém o objeto de proxy do ImageProxy - até que o programa chama o método Image.Show (), o programa precisa realmente chamar o método show () do objeto Bigimage, e o programa realmente cria o objeto Bigimage na época. Execute o programa acima e veja os resultados mostrados na Figura 6.
Figura 6. Melhorar o desempenho usando o modo proxy
Vendo os resultados em execução mostrados na Figura 6, os leitores devem ser capazes de concordar que o uso do modo proxy melhora o desempenho do sistema de obter objetos de imagem. Mas alguns leitores podem fazer perguntas: Quando um programa chama o método show () do objeto ImageProxy, ele também precisa criar um objeto Bigimage, mas a sobrecarga do sistema não foi realmente reduzida? É que essa sobrecarga do sistema está atrasada?
Podemos responder a essa pergunta das duas perspectivas a seguir:
Atrasar a criação do BigImage até que seja realmente necessário, pode garantir a operação suave do programa anterior e reduzir o tempo de sobrevivência do BIGIMAGE na memória, salvando a sobrecarga de memória do sistema de uma perspectiva macro.
Em alguns casos, talvez o programa nunca chame o método show () do objeto ImageProxy - o que significa que o sistema não precisa criar um objeto Bigimage. Nesse caso, o uso do modo proxy pode melhorar significativamente o desempenho da operação do sistema.
Totalmente semelhante, o Hibernate também usa o modo proxy para "atrasar" o tempo para carregar a entidade associada. Se o programa não precisar acessar a entidade associada, o programa não rastejará a entidade associada. Isso pode salvar a sobrecarga da memória do sistema e reduzir o tempo em que o hibernato carrega a entidade.
resumo
A carga preguiçosa de hibernação é essencialmente uma aplicação do modo proxy. Nos últimos anos, costumamos usar o modo proxy para reduzir a sobrecarga da memória do sistema e melhorar o desempenho do aplicativo. O Hibernate aproveita essa vantagem do modo proxy e combina Javassist ou CGLIB para gerar dinamicamente objetos proxy, o que adiciona flexibilidade ao modo proxy. O Hibernate dá a este uso um novo nome: carregamento preguiçoso. De qualquer forma, analisar e entender totalmente a implementação dessas estruturas de código aberto pode experimentar melhor as vantagens dos modelos de design clássicos.
Espero que a descrição deste artigo seja útil para a programação Java de todos com base na estrutura do Hibernate.