2017-05-04 1 views
0

J'utilise liquibase comme outil de migration pour mon projet actuel. Le problème est que j'essaie d'utiliser une séquence personnalisée pour les identifiants de génération automatique pour les entités, mais quelque chose ne va pas.Pourquoi la liquibase utilise-t-elle toujours sa propre séquence?

J'ai défini une table d'entité et séquence personnalisée comme similaire ci-dessous:

<databaseChangeLog> 
<changeSet id="create_orders_table" author="I am"> 
    <createTable tableName="ORDERS"> 
     <column name="id" type="SERIAL" valueComputed="SEQ_ORDERS.NEXTVAL" valueSequenceCurrent="SEQ_ORDERS.CURRENT" valueSequenceNext="SEQ_ORDERS.NEXTVAL" defaultValueSequenceNext="SEQ_ORDERS.NEXTVAL"> 
      <constraints primaryKey="true" unique="true"/> 
     </column> 
     <column name="number" type="VARCHAR(64)"/> 
     ... 
    </createTable> 
</changeSet> 
<changeSet id="add_sequence" author="I am"> 
    <createSequence sequenceName="SEQ_ORDERS" cycle="false" minValue="1" maxValue="9223372036854775807" startValue="1" incrementBy="1"/> 
</changeSet> 
</databaseChangeLog> 

Mais quand cette migration est appliquée, je vois la structure de tableau suivant dans postgresql:

CREATE TABLE public.orders 
(
id integer NOT NULL DEFAULT nextval('orders_id_seq'::regclass), 
"number" character varying(64), 
... 
CONSTRAINT pk_orders PRIMARY KEY (id) 
) 
WITH (
OIDS=FALSE 
); 

Première imprévisible chose pour moi ici est pourquoi liquibase a écrit son propre orders_id_seq au lieu de mon SEQ_ORDERS?

Ensuite, j'écrit un code JPA typique et il est test à l'aide du printemps:

@Entity(name = "ORDERS") 
public class Order { 

    @Id 
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "ORDERS_ID_GEN") 
    @SequenceGenerator(name = "ORDERS_ID_GEN", sequenceName = "SEQ_ORDERS") 
    private long id; 

    private String number; 
    //... getters,setters and other stuff 
} 

public interface OrderRepository extends JpaRepository<Order,Long> {} 

test

@RunWith(SpringRunner.class) 
@DataJpaTest 
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) 
@Rollback(value = false) 
public class OrderRepositoryTest { 

    @Autowired 
    private TestEntityManager testEntityManager; 

    @Autowired 
    private OrderRepository orderRepository; 


    @Test 
    public void testThatIdsOfOrdersGenerateCorrectly(){ 


     Order orderOne = new Order(); 
     orderOne.setNumber("order-n-01"); 

     Order orderTwo = new Order(); 
     orderTwo.setNumber("order-n-02"); 

     Order orderThree = new Order(); 
     orderThree.setNumber("order-n-03"); 

     testEntityManager.persist(orderOne); 
     testEntityManager.persist(orderTwo); 
     testEntityManager.persist(orderThree); 

     Assert.assertThat(orderRepository.findOne(new Long(1)),is(orderOne)); 
     Assert.assertThat(orderRepository.findOne(new Long(2)),is(orderTwo)); 
     Assert.assertThat(orderRepository.findOne(new Long(3)),is(orderThree)); 

    } 

} 

Dans les journaux de test, je vois:

Hibernate: select nextval ('seq_orders') 
... 
java.lang.AssertionError: 
Expected: is <Order(id=50, number=order-n-01, ...> 
    but: was null 

Après test est terminé en DB J'ai vu trois commandes avec id = 50,51,52

Première fois que je pensais que orders_id_seq incrémente la valeur sur 50 à chaque fois. j'étais très surpised quand j'ai vu non modifié orders_id_seq après le test (valeur actuelle = 1), mais mon SEQ_ORDERS a été augmentée sur 1.

Quand je lance le test encore une fois je suis arrivé trois commandes avec ids 100.101.102

pourrait Quelqu'un m'explique ce qui se passe ici? Et comment faire liquibase & postgresql faire les bonnes choses?

J'utilise: PostgreSQL 9.6 (org.postgresql.9.4.1212 pilote JDBC), Liquibase 3.5.3, Spring Boot 1.5.2

Spring Boot test Config:

spring.jpa.hibernate.ddl-auto=none 
spring.jpa.generate-ddl=false 
spring.jpa.database=postgresql 

spring.datasource.initialize=false 
spring.datasource.username=postgres 
spring.datasource.password=******** 
spring.datasource.url=jdbc:postgresql://localhost:5432/ORDERS_TEST 
spring.datasource.driver-class-name=org.postgresql.Driver 

liquibase.change-log=classpath:/db/changelog/changelog-master.xml 

Répondre

1

Si vous spécifiez serial en tant que type de données, vous ne devez pas spécifier de valeur par défaut, car Postgres will already do that for you. La séquence et orders_id_seq et la valeur par défaut est créée par Postgres automatiquement lorsque vous utilisez serial c'est pas créé par Liquibase. Par conséquent, si vous souhaitez utiliser votre propre séquence (quelle qu'en soit la raison), spécifiez la colonne integer et spécifiez defaultValueComputed en utilisant nextval(). Vous devez également créer la séquence avant de créer la table.

également la syntaxe pour obtenir la valeur suivante n'est pas sequence.nextval mais nextval('sequence') dans Postgres:

<databaseChangeLog> 
<changeSet id="add_sequence" author="I am"> 
    <createSequence sequenceName="SEQ_ORDERS" cycle="false" minValue="1" maxValue="9223372036854775807" startValue="1" incrementBy="1"/> 
</changeSet> 

<changeSet id="create_orders_table" author="I am"> 
    <createTable tableName="ORDERS"> 
     <column name="id" type="integer" defaultValueComputed="nextval('seq_orders')> 
      <constraints primaryKey="true" unique="true"/> 
     </column> 
     <column name="number" type="VARCHAR(64)"/> 
     ... 
    </createTable> 
</changeSet> 
</databaseChangeLog> 
+0

Ouais, ce changelog fait postgreSQL utiliser ma séquence mais je m'y suis 50,51,52 ids de toute façon. – rvit34

+0

@ rvit34: probablement quelque chose qui ne va pas avec le mapping dans votre couche d'obfuscation - je n'utilise pas Hibernate donc je ne peux pas vous aider avec ça). Vous voudrez peut-être poser une question distincte pour cela (une question sur l'OS ne devrait contenir qu'une seule question). Mais cela ressemble à une sorte de mise en cache dans Hibernate. Ou avez-vous créé la séquence avec 'cache =" 50 "'? –

+0

Comme je le vois dans PgAdmin, cache de mon SEQ_ORDERS est 1 – rvit34