JavaRush /Blogue Java /Random-PT /Entidades JPA e relacionamentos de banco de dados
Nikita Koliadin
Nível 40
Днепр

Entidades JPA e relacionamentos de banco de dados

Publicado no grupo Random-PT

Entidades JPA e relacionamentos de banco de dados

Bom dia, colegas!
Entidades JPA e relacionamentos de banco de dados - 1
Este material é destinado a quem já possui entendimento sobre organização de bancos de dados (doravante simplesmente DB - "Banco de Dados"), conhecimento mínimo de como funciona o Mapeamento Objeto-Relacional (doravante simplesmente ORM ), e suas implementações, como Hibernate/JPA . Se você não está familiarizado com isso, aconselho que comece pelo JDBC , e só depois passe para o modelo ORM. Eu avisei e não assumo responsabilidade por sua psique depois de ler este artigo sem a devida preparação! :) Vamos começar a lidar com tudo em ordem. Primeiramente vamos nos aprofundar um pouco na teoria, só um pouquinho. Em segundo lugar, vamos descobrir como fazer essa merda no Java favorito de todos. Também escreveremos com você uma folha de dicas do projeto, que consolidará nosso entendimento sobre o tema e servirá como modelo para COMO o mapeamento deve ser feito . Então, vamos fazê-lo!

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 :
  1. Um a um
  2. Um para muitos
  3. Muitos para Um
  4. Muitos para muitos
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 .
  1. 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:

    JPA Entities and DB Relationships - 2

    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.

    JPA Entities and DB Relationships - 3

    A seguir, vamos configurar o circuito. Esta etapa é opcional, mas recomendada se você tiver vários esquemas no banco de dados.

    JPA Entities and DB Relationships - 4

    Aplique as configurações e no final devemos obter algo assim:

    JPA Entities and DB Relationships - 5
  2. 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:

    JPA Entities and DB Relationships - 6

    Resultado da execução no console:

    JPA Entities and DB Relationships - 7

    Resultado no banco de dados:

    JPA Entities and DB Relationships - 8
  3. Vejamos um diagrama de nossas tabelas. Para fazer isso, RMB em nosso banco de dados:

    JPA Entities and DB Relationships - 9

    Resultado:

    JPA Entities and DB Relationships - 10

    No diagrama UML podemos ver todas as chaves primárias e estrangeiras, também vemos a conexão entre nossas tabelas.

  4. 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.

  5. 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;
    }
Vamos descobrir em ordem:
  1. Todos os campos da classe repetem os atributos da entidade do banco de dados.
  2. @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.
  3. @Entity diz que a classe fornecida é uma entidade e está associada a uma entidade de banco de dados.
  4. @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.
  5. @Table (name = "AUTHOR") vincula a classe Book à tabela DB AUTHOR.
  6. @Id diz que este campo é a chave primária.
  7. @GeneratedValue (strategy = GenerationType.IDENTITY) – estratégia de geração de chave primária.
  8. @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.
  9. @OneToOne diz que o campo fornecido é um campo de relacionamento um para um.
  10. @JoinColumn (name = "BOOK_ID", unique = true, nullable = false) - será criada uma coluna BOOK_ID, que é única e não nula.
No verso (na classe Book ) também precisamos fazer uma conexão One-to-One e indicar o campo em que ocorre o mapeamento. @OneToOne(mappedBy = "book") - neste exemplo, este é o campo book da classe Author. A própria JPA irá vinculá-los. À primeira vista pode parecer uma confusão de anotações, mas na verdade é muito conveniente e com a experiência você irá adicioná-las automaticamente, sem nem pensar.
  1. 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>
Descrição das propriedades :
  1. hibernate.dialect é o dialeto do SGBD que escolhemos.
  2. hibernate.connection.driver_class - Classe de driver do nosso banco de dados.
  3. hibernate.connection.url - utl do nosso banco de dados. Você pode começar desde o primeiro ponto onde configuramos o banco de dados.
  4. hibernate.connection.username - nome de usuário do banco de dados.
  5. hibernate.connection.password — Senha do usuário do banco de dados.
  6. hibernate.hbm2ddl.auto - configurando a geração de tabelas. Se atualizar, então não gera se já tiver sido criado, apenas o atualiza.
  7. hibernate.show_sql - se deseja mostrar consultas de banco de dados.
  8. hibernate.format_sql - se deve formatar consultas de banco de dados. Caso contrário, todos estarão na mesma linha. Eu recomendo ligá-lo.
  9. 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.
  10. 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.
  11. hibernate.jdbc.batch_size — Tamanho máximo do lote.
  12. hibernate.jdbc.fetch_size — Tamanho máximo de busca.
  13. hibernate.order_inserts - permite inserções dinâmicas.
  14. hibernate.order_updates – Permite atualizações dinâmicas.
  15. hibernate.jdbc.batch_versioned_data - permite lote. Olhe para o seu SGBD: nem todo mundo suporta isso.
  16. classe de mapeamento - classes que são nossas entidades. Tudo precisa ser listado.
  1. Agora nossa essência deve ser determinada. Podemos verificar isso na aba persistência:

    JPA Entities and DB Relationships - 11

    Resultado:

    JPA Entities and DB Relationships - 12
  2. Também precisamos configurar a atribuição de dados:

    JPA Entities and DB Relationships - 13 JPA Entities and DB Relationships - 14

    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.
  1. Como resultado da execução do script de inicialização, obtemos o seguinte:

    JPA Entities and DB Relationships - 15

    Você sente a diferença com a tabela anterior?

  2. Diagrama:

    JPA Entities and DB Relationships - 16

    Relacionamento um-para-muitos – um autor pode ter vários livros. A entidade esquerda corresponde a uma ou mais entidades direitas.

  3. 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.

  4. Nada mudou muito em hibernate.cfg.xml .

  5. Persistência:

    JPA Entities and DB Relationships - 17

Relacionamento muitos para um

Como Muitos para Um é uma imagem espelhada de Um para Muitos, haverá poucas diferenças. Link para filial aqui .
  1. Como resultado da execução do script de inicialização, obtemos o seguinte resultado:

    JPA Entities and DB Relationships - 18
  2. Diagrama:

    JPA Entities and DB Relationships - 19
  3. 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 .

  4. hibernate.cfg.xml

  5. Persistência:

    JPA Entities and DB Relationships - 20

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 .
  1. 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:

    JPA Entities and DB Relationships - 21
  2. Diagrama:

    JPA Entities and DB Relationships - 22

    Em nosso exemplo, verifica-se que um livro pode ter muitos autores e um autor pode ter muitos livros. Eles podem se sobrepor.

  3. 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.

  4. 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).

  5. Persistência:

    JPA Entities and DB Relationships - 23

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

Meus artigos anteriores: PS Pode haver erros e lacunas no texto. PPS O autor estava fumando algo estranho enquanto escrevia este artigo. Obrigado pela sua atenção!
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION