JPA SequenceGenerator

EXISTE UMA NOVA VERSÃO DESSE POST.
CLICK AQUI: http://uaihebert.com/?p=1622&page=6

Olá pessoal, tudo bem?

Vamos falar hoje sobre o modo de geração de Primary Key chamado “SequenceGenerator“. No ultimo post (JPA TableGenerator – Chave Primária Simples) falamos sobre o TableGenerator e como resolver o fato de termos apenas uma Sequence para todas as tabelas.

Que tal hoje vermos como gerar uma Sequence por Tabela (Classe)? Caso o fato de ter uma tabela para gerar códigos tenha deixado sua classe nada elegante, podemos arrumar esse pequeno “incomodo” alterando de TableGenerator para SequenceGenerator.

Tendo uma Sequence por tabela nos trará o mesmo resultado de termos uma tabela para guardar o valor da próxima PK de cada classe.

Lembro que estaremos utilizando o Hibernate e sua implementação do JPA. Caso você queira, poderá utilizar qualquer outra implementação do JPA que forneça uma criação automática de schema.

Irei continuar exatamente do último post (JPA TableGenerator – Chave Primária Simples), onde iremos apenas realizar, a alteração nas classes User e Dog, de TableGenerator para SequenceGenerator.

Colocarei abaixo como estão nossas classes e como ficarão. Caso você queira montar o ambiente para que você possa executar o código abaixo, basta ir no tópico JPA TableGenerator – Chave Primária Simples que você verá o passo a passo.

Como estão nossas classesconfigurações hoje:

<?xml version="1.0" encoding="UTF-8"?>

<persistence version="2.0"
    xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">

    <persistence-unit name="Hello" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>

        <properties>
            <property name="hibernate.show_sql" value="true" />
            <property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver" />
            <property name="javax.persistence.jdbc.url" value="jdbc:postgresql://localhost:5432/postgres"/>
            <property name="javax.persistence.jdbc.user" value="postgres" />
            <property name="javax.persistence.jdbc.password" value="postgres" />
            <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>
            <property name="hibernate.hbm2ddl.auto" value="update"/>
            <property name="hibernate.default_schema" value="hello"/>
        </properties>
    </persistence-unit>
</persistence>
package com;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.TableGenerator;

@Entity
@Table(name="USER")
public class User{
    /**
     *
     */
    private static final long serialVersionUID = 1L;

    @TableGenerator(name="USER_GENERATOR",
            table="GENERATED_KEYS",
            pkColumnName="PK_COLUMN",
            valueColumnName="VALUE_COLUMN",
            pkColumnValue="USER_ID",
            allocationSize=1
    )
    @Id
    @GeneratedValue(strategy = GenerationType.TABLE, generator="USER_GENERATOR")
    private int id;

    @Column
    private String name;

    @Column
    private String password;

    @Override
    public boolean equals(Object obj) {
        if(obj instanceof User){
            User user = (User) obj;
            return user.getId() == this.getId();
        }

        return false;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}
package com;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.TableGenerator;

@Entity
@Table(name="DOG")
public class Dog {

    @TableGenerator(name="DOG_GENERATOR",
            table="GENERATED_KEYS",
            pkColumnName="PK_COLUMN",
            valueColumnName="VALUE_COLUMN",
            pkColumnValue="DOG_ID",
            allocationSize=1
    )
    @Id
    @GeneratedValue(strategy = GenerationType.TABLE, generator="DOG_GENERATOR")
    private int id;
    @Column
    private String name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
package com;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public class Main {

    /**
     * @param args
     */
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("Hello");
        EntityManager em = emf.createEntityManager();

        try {
            em.getTransaction().begin();

            User user = new User();
            user.setName("abc123");
            user.setPassword("abc123");
            em.persist(user);

            Dog dog = new Dog();
            dog.setName("pineapple");
            em.persist(dog);

            em.getTransaction().commit();
        }
        catch (Exception e) {
            em.getTransaction().rollback();
            e.printStackTrace();
        }
        finally{
            emf.close();
        }

        System.out.println("It is over");
    }
}

O código com a anotação @TableGenerator atualmente utiliza essa estratégia. Realmente são várias linhas de código para uma função simples.

Para alterar o seu código para SequenceGenerator basta alterar o acima código para:

package com;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;

@Entity
@Table(name="USER")
@SequenceGenerator(name="USER_SEQUENCE", sequenceName="USER_SEQUENCE", allocationSize=1, initialValue=0)
public class User{
    /**
     *
     */
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator="USER_SEQUENCE")
    private int id;

    @Column
    private String name;

    @Column
    private String password;

    @Override
    public boolean equals(Object obj) {
        if(obj instanceof User){
            User user = (User) obj;
            return user.getId() == this.getId();
        }

        return false;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}
package com;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;

@Entity
@Table(name="DOG")
@SequenceGenerator(name="DOG_SEQUENCE", sequenceName="DOG_SEQUENCE", allocationSize=1, initialValue=0)
public class Dog {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator="DOG_SEQUENCE")
    private int id;
    @Column
    private String name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

As linhas de códigos que contém a anotação @SequenceGenerator representam a alteração necessária para que nossa classe fosse da estratégia de TableGenerator para SequenceGenerator.

Vou explicar sobre os parâmetros do SequenceGenerator:

  • name = nome da seqüência. Esse é o nome utilizado pelo ID ao localizar a seqüência criada.
  • sequenceName = nome da seqüência que será criada(ou utilizada caso já exista) no banco de dados.
  • allocationSize = O valor que será acrescido a cada novo registro. Estamos adicionando sempre um, caso fosse 2 o primeiro usuário criado teria seu ID 2 o segundo 4 e assim vai.
  • initialValue = valor inicial da sequence.

Vamos executar a classe Main e ver o resultado?
Imagem do Console.

Repare que já foi feita a consulta na sequence para saber qual o próximo valor. A sequence foi criada no momento que executamos o sistema, e isso ocorre apenas quando a sequence não existe no banco de dados ainda.
Imagem Sequences criadas.

Espero que esse post possa ter lhe ajudado.

Dúvidas/Críticas/Sugestões basta comentar.

Até a próxima

4 thoughts on “JPA SequenceGenerator

  1. Muito bom !
    Me diz uma coisa, @SequenceGenerator não funciona no mysql ?

    Erro:

    Caused by: org.hibernate.MappingException: org.hibernate.dialect.MySQL5InnoDBDialect does not support sequences

    • Olá Yeslei,

      Se não me engano esse erro se dá ao fato que MySQL não suporta SequenceGenerator.

      Tomei como base a mensagem de erro exibida: Caused by: org.hibernate.MappingException: org.hibernate.dialect.MySQL5InnoDBDialect does not support sequences

      Você terá que tomar outra abordagem de geração de ID.

  2. O problema é que desta forma se você for usar uma replicação de banco de dados essa solução não funcionaria.

    Eu já pensei em várias possibilidades e acho que a forma mais proximo do perfeito que encontrei seria um banco local (sqllite ou um nosql da vida) somente para gerar chaves, desta forma seria mais simples escalar o banco.

    Tem uma ideia melhor ?

Leave a Comment