Hibernate jumping id string

0

I generated a project by JHipster which uses Java , Spring and Hibernate in the backend, I created a class as follows:

Liquibase:

 <changeSet id="20160504131602" author="jhipster">
        <createTable tableName="grupo">
            <column name="id" type="bigint" autoIncrement="${autoIncrement}">
                <constraints primaryKey="true" nullable="false"/>
            </column>
            <column name="nm_grupo" type="varchar(35)">
                <constraints unique="true" nullable="false" />
            </column>
 </createTable>

Domain:

@Entity
@Table(name = "grupo")
@Document(indexName = "grupo")
public class Grupo implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotNull
    @Size(min = 1, max = 35)
    @Column(name = "nm_grupo", length = 35, unique = true, nullable = false)
    private String nmGrupo;

The issue is that when I command to save a record (Class rest):

@RequestMapping(value = "/grupos",
        method = RequestMethod.POST,
        produces = MediaType.APPLICATION_JSON_VALUE)
    @Timed
    public ResponseEntity<Grupo> createGrupo(@Valid @RequestBody Grupo grupo) throws URISyntaxException {
        log.debug("REST request to save Grupo : {}", grupo);
        if (grupo.getId() != null) {
            return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert("grupo", "idexists", "A new grupo cannot already have an ID")).body(null);
        }
        Grupo result = grupoService.save(grupo);
        return ResponseEntity.created(new URI("/api/grupos/" + result.getId()))
            .headers(HeaderUtil.createEntityCreationAlert("grupo", result.getId().toString()))
            .body(result);
    }

That in turn calls the save function of the implementation class:

public Grupo save(Grupo grupo) {
        log.debug("Request to save Grupo : {}", grupo);
        Grupo result = grupoRepository.save(grupo);
        grupoSearchRepository.save(result);
        return result;
    }

Saving the object physically in the database:

public interface GrupoRepository extends JpaRepository<Grupo,Long> {

}

If I have an object saved with a name already registered in the database, hibernate returns the error:

  

ERROR: duplicate key value violates unique constraint "group_nm_group_key" **

This error I already dealt, however when I save a new record the generated code for the id is jumped on +1, thus getting a lost id.

Ex:

  • Save the group: A (generated id: 1)
  • Save the group: A (error because the value already exists in the database)
  • Save the group: AS (generated id: 3)
  • Notice that I lost id 2.

    Does anyone know an elegant way to solve this problem, without having to create a new query before the save to verify that the name has already been registered?

        
    asked by anonymous 06.05.2016 / 14:02

    2 answers

    1

    This identifier is probably being generated by the backend (Postgresql).

    This phenomenon happens because SEQUENCES in Postgresql is resistant to ROLLBACKs within transactions.

    Probably the autoIncrement of the id column is using a SEQUENCE object in the database:

    <column name="id" type="bigint" autoIncrement="${autoIncrement}">
        <constraints primaryKey="true" nullable="false"/>
    </column>
    

    SEQUENCES are intended to generate unique identifiers - not necessarily identifiers that are strictly sequential!

    You should not use SEQUENCES when you need a sequence with no breaks between identifiers.

    For example, SEQUENCES ensures that if two clients simultaneously try to get a value from a string (using nextval() ), each client will receive a different sequence value. If one of these clients subsequently aborts your transaction using a ROLLBACK , the sequence value that was generated for that client will be lost and this will create a gap in the sequence.

    Here's an example of how a SEQUENCE works within a transaction:

    -- Criação do Objeto de SEQUENCE
    CREATE SEQUENCE sq_teste;
    
    -- Inicializa a Sequence
    SELECT nextval('sq_teste');
    
    -- Verifica o valor da sequencia antes de entrar na transação
    SELECT currval('sq_teste'); -- ID=1
    
    BEGIN;
    SELECT nextval('sq_teste'); -- ID=2
    SELECT nextval('sq_teste'); -- ID=3
    SELECT nextval('sq_teste'); -- ID=4
    ROLLBACK;
    
    -- Verifica o valor da sequencia após ROLLBACK da transação
    SELECT currval('sq_teste'); -- ID: 4!
    
    --- fim-de-arquivo ---
    

    Note that even the% s of% of the sequence value did not return to what it was before the transaction started.

    Now the question is mine: Why do registered IDs need to respect a growing sequence without breaks?

    I hope I have helped!

        
    11.05.2016 / 23:40
    0

    Unfortunately, I have not found anywhere in Hibernate's documentation and a way to do this automatically.

    I had to create the query in the hand before the insert to check if the name already existed in the database.

        
    11.05.2016 / 14:16