Entidades JPA e relacionamentos de banco de dados
Bom dia, colegas!O que é Entidade?
Uma Entidade é um objeto da vida real (por exemplo, um carro) que possui atributos (portas, RODAS , motor). Entidade BD: Neste caso nossa entidade está armazenada em um BD, tudo é simples. Por que e como colocamos o carro no banco de dados - veremos isso mais tarde.O que são relacionamentos de banco de dados?
Há muito tempo, em um reino distante, foi criado um banco de dados relacional . Neste BD, os dados foram apresentados em forma de tabelas. Mas até o burro do Shrek entendeu que era preciso criar um mecanismo para interligar essas mesas. Como resultado, surgiram 4 relacionamentos de banco de dados : Se você está vendo tudo isso pela primeira vez, volto a avisar - vai piorar: pense em dar um passeio. Analisaremos todas essas relações usando um exemplo e entenderemos a diferença entre elas.Exemplo de terror
Teremos um projeto que terá 5 ramificações: master, onde haverá a descrição do projeto, e 1 ramificação para cada relacionamento do BD. Cada branch conterá scripts SQL para criar um banco de dados e preenchê-lo com dados de teste, além de uma classe Entity com mapeamento de anotação. Haverá também um arquivo de configuração do Hibernate para cada branch. Usarei o banco de dados incorporado H2 para o projeto, para não me distrair com aspectos individuais do banco de dados em nuvem ou banco de dados externo. Seguindo o link, instale o H2 DB no seu aspirador. Descreverei cada etapa em um ramo, o resto são apenas os pontos-chave. No final faremos um resumo. Ir. Este é um link para o branch master do meu projeto.Relacionamento um para um
Link para filial aqui .-
Precisamos conectar o H2 DB ao nosso projeto. Aqui precisamos enfatizar que precisamos do Ultimate IDEA para trabalhar confortavelmente com DB e outras coisas. Se você já o possui, vá diretamente para a conexão do banco de dados. Vá para a guia Banco de dados e faça como na captura de tela:
Em seguida, passamos para as configurações do banco de dados. Você pode inserir seus dados, e até mesmo seu SGBD; repito, utilizo H2 DB para simplificar.
A seguir, vamos configurar o circuito. Esta etapa é opcional, mas recomendada se você tiver vários esquemas no banco de dados.
Aplique as configurações e no final devemos obter algo assim:
-
Criamos o banco de dados e configuramos o acesso a ele no IDEA. Agora você precisa criar tabelas nele e preenchê-lo com alguns dados. Por exemplo, pegarei duas entidades: Autor e Livro. Um livro pode ter um autor, pode ter vários autores ou pode não ter um. Neste exemplo criaremos todos os tipos de conexões. Mas neste ponto – relacionamento um-para-um. Vamos criar o script correspondente que cria tabelas de banco de dados :
DROP TABLE IF EXISTS PUBLIC.BOOK; CREATE TABLE PUBLIC.BOOK ( ID INTEGER NOT NULL AUTO_INCREMENT, NAME VARCHAR(255) NOT NULL, PRINT_YEAR INTEGER(4) NOT NULL, CONSTRAINT BOOK_PRIMARY_KEY PRIMARY KEY (ID) ); DROP TABLE IF EXISTS PUBLIC.AUTHOR; CREATE TABLE PUBLIC.AUTHOR ( ID INTEGER NOT NULL AUTO_INCREMENT, FIRST_NAME VARCHAR(255) NOT NULL, SECOND_NAME VARCHAR(255) NOT NULL, BOOK_ID INTEGER NOT NULL UNIQUE, CONSTRAINT AUTHOR_PRIMARY_KEY PRIMARY KEY (ID), CONSTRAINT BOOK_FOREIGN_KEY FOREIGN KEY (BOOK_ID) REFERENCES BOOK (ID) );
E vamos executá-lo:
Resultado da execução no console:
Resultado no banco de dados:
-
Vejamos um diagrama de nossas tabelas. Para fazer isso, RMB em nosso banco de dados:
Resultado:
No diagrama UML podemos ver todas as chaves primárias e estrangeiras, também vemos a conexão entre nossas tabelas.
-
Vamos escrever um script que preencha nosso banco de dados com dados de teste:
INSERT INTO PUBLIC.BOOK (NAME, PRINT_YEAR) VALUES ('First book', 2010), ('Second book', 2011), ('Third book', 2012); INSERT INTO PUBLIC.AUTHOR (FIRST_NAME, SECOND_NAME, BOOK_ID) VALUES ('Pablo', 'Lambado', 1), ('Pazo', 'Zopa', 2), ('Lika', 'Vika', 3);
Quero dizer, o que acontece? O relacionamento um para um é necessário quando a entidade de uma tabela está relacionada a uma entidade de outra (ou não está relacionada se NOT NULL for removido de BOOK_ID). Em nosso exemplo, um livro DEVE ter um autor. Não há outro jeito.
-
Agora o mais interessante é como conectar uma classe Java com entidades de banco de dados? Muito simples. Vamos criar duas classes Livro e Autor. Usando um exemplo, analisarei a classe 1 e os principais campos de comunicação. Vamos pegar a classe Autor como exemplo :
@Data @Entity @DynamicInsert @DynamicUpdate @Table(name = "AUTHOR") public class Author { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "ID", nullable = false) private Long id; @Column(name = "FIRST_NAME", nullable = false) private String firstName; @Column(name = "SECOND_NAME", nullable = false) private String secondName; @OneToOne @JoinColumn(name = "BOOK_ID", unique = true, nullable = false) private Book book; }
- Todos os campos da classe repetem os atributos da entidade do banco de dados.
- @Data (de Lombok ) diz que para cada campo um getter e um setter serão criados, iguais, o código hash será substituído e um método toString será gerado.
- @Entity diz que a classe fornecida é uma entidade e está associada a uma entidade de banco de dados.
- @DynamicInsert e @DynamicUpdate dizem que inserções e atualizações dinâmicas serão realizadas no banco de dados. Estas são configurações mais profundas do Hibernate que serão úteis para que você tenha o lote CORRETO.
- @Table (name = "AUTHOR") vincula a classe Book à tabela DB AUTHOR.
- @Id diz que este campo é a chave primária.
- @GeneratedValue (strategy = GenerationType.IDENTITY) – estratégia de geração de chave primária.
- @Column (name = "ID", nullable = false) associa um campo a um atributo do banco de dados e também diz que o campo do banco de dados fornecido não pode ser nulo. Isto também é útil ao gerar tabelas de entidades. O processo inverso de como criamos nosso projeto agora, é necessário nos bancos de dados de teste para testes unitários.
- @OneToOne diz que o campo fornecido é um campo de relacionamento um para um.
- @JoinColumn (name = "BOOK_ID", unique = true, nullable = false) - será criada uma coluna BOOK_ID, que é única e não nula.
-
Agora vamos configurar o Hibernate. Para fazer isso, crie um arquivo hibernate.cfg.xml :
<?xml version='1.0' encoding='utf-8'?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="hibernate.dialect">org.hibernate.dialect.H2Dialect</property> <property name="hibernate.connection.driver_class">org.h2.Driver</property> <property name="hibernate.connection.url">jdbc:h2:~/db/onetoone</property> <property name="hibernate.connection.username">root</property> <property name="hibernate.connection.password"/> <property name="hibernate.hbm2ddl.auto">update</property> <property name="hibernate.show_sql">true</property> <property name="hibernate.format_sql">true</property> <property name="hibernate.use_sql_comments">true</property> <property name="hibernate.generate_statistics">true</property> <property name="hibernate.jdbc.batch_size">50</property> <property name="hibernate.jdbc.fetch_size">50</property> <property name="hibernate.order_inserts">true</property> <property name="hibernate.order_updates">true</property> <property name="hibernate.jdbc.batch_versioned_data">true</property> <mapping class="com.qthegamep.forjavarushpublication2.entity.Book"/> <mapping class="com.qthegamep.forjavarushpublication2.entity.Author"/> </session-factory> </hibernate-configuration>
- hibernate.dialect é o dialeto do SGBD que escolhemos.
- hibernate.connection.driver_class - Classe de driver do nosso banco de dados.
- hibernate.connection.url - utl do nosso banco de dados. Você pode começar desde o primeiro ponto onde configuramos o banco de dados.
- hibernate.connection.username - nome de usuário do banco de dados.
- hibernate.connection.password — Senha do usuário do banco de dados.
- hibernate.hbm2ddl.auto - configurando a geração de tabelas. Se atualizar, então não gera se já tiver sido criado, apenas o atualiza.
- hibernate.show_sql - se deseja mostrar consultas de banco de dados.
- hibernate.format_sql - se deve formatar consultas de banco de dados. Caso contrário, todos estarão na mesma linha. Eu recomendo ligá-lo.
- hibernate.use_sql_comments - comenta consultas ao banco de dados. Se for uma Inserção, um comentário será escrito acima da solicitação informando que a solicitação é do tipo Insert.
- hibernate.generate_statistics - gera logs. Eu recomendo e recomendo configurar o log ao máximo. A leitura dos logs aumentará suas chances de funcionar corretamente com o ORM.
- hibernate.jdbc.batch_size — Tamanho máximo do lote.
- hibernate.jdbc.fetch_size — Tamanho máximo de busca.
- hibernate.order_inserts - permite inserções dinâmicas.
- hibernate.order_updates – Permite atualizações dinâmicas.
- hibernate.jdbc.batch_versioned_data - permite lote. Olhe para o seu SGBD: nem todo mundo suporta isso.
- classe de mapeamento - classes que são nossas entidades. Tudo precisa ser listado.
-
Agora nossa essência deve ser determinada. Podemos verificar isso na aba persistência:
Resultado:
-
Também precisamos configurar a atribuição de dados:
Resultados: Fizemos o mapeamento um para um. O material tem caráter meramente informativo, os detalhes estão nas referências.
Relacionamento um-para-muitos
Link para filial aqui . Não vou mais postar o código no artigo, pois já é muito longo. Vemos todo o código no GitHub.-
Como resultado da execução do script de inicialização, obtemos o seguinte:
Você sente a diferença com a tabela anterior?
-
Diagrama:
Relacionamento um-para-muitos – um autor pode ter vários livros. A entidade esquerda corresponde a uma ou mais entidades direitas.
-
A diferença no mapeamento estará nas anotações e campos:
Um campo aparece na classe Autor :
@OneToMany(fetch = FetchType.LAZY, mappedBy = "author") private Set<Book> books;
Já é um conjunto, pois podemos ter vários livros. @OneToMany está falando sobre tipo de relacionamento. FetchType.Lazy diz que não precisamos carregar a lista inteira de livros se ela não estiver especificada na solicitação. Também deve ser dito que este campo NÃO PODE ser adicionado a toString, caso contrário começaremos a fumar um StackOverflowError. Meu amado Lombok cuida disso:
@ToString(exclude = "books")
Na aula do Livro , fazemos feedback muitos para um:
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) @JoinColumn(name = "AUTHOR_ID", nullable = false) private Author author;
Aqui concluímos que Um para Muitos é a imagem espelhada de Muitos para Um e vice-versa. Deve-se enfatizar que o Hibernate não sabe nada sobre comunicações bidirecionais. Para ele, são duas conexões diferentes: uma em uma direção, outra na direção oposta.
-
Nada mudou muito em hibernate.cfg.xml .
-
Persistência:
Relacionamento muitos para um
Como Muitos para Um é uma imagem espelhada de Um para Muitos, haverá poucas diferenças. Link para filial aqui .-
Como resultado da execução do script de inicialização, obtemos o seguinte resultado:
-
Diagrama:
-
A diferença no mapeamento estará nas anotações e campos:
Não existe mais um conjunto na classe Autor , pois ele foi movido para a classe Livro .
-
Persistência:
Relacionamento muitos-para-muitos
Vamos passar para o relacionamento mais interessante. Esta relação, de acordo com todas as regras de decência e indecência, é criada através de uma tabela adicional. Mas esta tabela não é uma entidade. Interessante, certo? Vamos dar uma olhada nessa merda. Link para filial aqui .-
Observe o script de inicialização , uma tabela HAS adicional aparece aqui. Obtemos algo como o autor tem um livro.
Como resultado da execução do script, obteremos as seguintes tabelas:
-
Diagrama:
Em nosso exemplo, verifica-se que um livro pode ter muitos autores e um autor pode ter muitos livros. Eles podem se sobrepor.
-
As classes de mapeamento terão conjuntos dentro das classes. Mas como eu disse, a tabela HAS não é uma entidade.
Classe de autor :
@ManyToMany @JoinTable(name = "HAS", joinColumns = @JoinColumn(name = "AUTHOR_ID", referencedColumnName = "ID"), inverseJoinColumns = @JoinColumn(name = "BOOK_ID", referencedColumnName = "ID") ) private Set<Book> books;
@ManyToMany é um tipo de relacionamento.
@JoinTable - é exatamente isso que conectará o atributo a uma tabela HAS adicional. Nele especificamos dois atributos que apontarão para as chaves primárias de duas entidades.
Aula de livro :
@ManyToMany(fetch = FetchType.LAZY, mappedBy = "books") private Set<Author> authors;
Aqui indicamos FetchType e o campo que usaremos para mapear.
-
Nosso hibernate.cfg.xml novamente permaneceu inalterado (não levo em consideração o fato de termos criado um novo banco de dados para cada filial).
-
Persistência:
Interrogatório
Portanto, examinamos superficialmente os tipos de relacionamentos de banco de dados e descobrimos como implementá-los no modelo ORM. Escrevemos um projeto de teste que demonstra todas as conexões e descobrimos como configurar o hibernate/jpa. Ufa.Links Úteis
- Na verdade, o projeto em si
- Filial um para um
- Filial um para muitos
- Filial muitos para um
- Ramo muitos para muitos
- Leia-o
- E leia
GO TO FULL VERSION