为什么 liquibase 总是使用自己的序列?

Why liquibase does always use its own sequence?

我正在使用 liquibase 作为我当前项目的迁移工具。

问题是我正在尝试为实体的自动生成 ID 使用自定义序列,但出现了错误。

我定义了一个实体 table 和自定义序列,如下所示:

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

但是当应用此迁移时,我在 postgresql 中看到以下 table 结构:

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
);

对我来说第一个不可预测的table 事情是为什么 liquibase 写了它自己的 orders_id_seq 而不是我的 SEQ_ORDERS

接下来我编写了一个典型的 JPA 代码并使用 Spring:

对其进行了测试
@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> {}

测试

@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));

    }

}

在测试日志中我看到:

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

测试完成后在数据库中我看到了三个id=50,51,52的订单

我第一次想到 orders_id_seq 每次都会增加 50 的值。 当我在测试后看到未修改的 orders_id_seq(当前值=1)时,我感到非常惊讶,但我的 SEQ_ORDERS 增加了 1.

当我再次 运行 测试时,我得到了接下来的三个 ID 为 100,101,102

的订单

谁能给我解释一下这到底是怎么回事? 以及如何让 liquibase 和 postgresql 做正确的事情?

我正在使用:PostgreSQL 9.6(org.postgresql.9.4.1212 JDBC 驱动程序),Liquibase 3.5.3,Spring Boot 1.5.2

Spring 启动测试配置:

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

如果指定 serial 作为数据类型,则不应指定任何默认值,因为 Postgres will already do that for you。序列和 orders_id_seq 以及默认值是由 Postgres 在您使用 serial 时自动创建的,它是由 而不是 创建的Liquibase.

因此,如果您想使用自己的序列(无论出于何种原因),请将列指定为 integer 并使用 nextval() 指定 defaultValueComputed。您还需要在创建 table 之前创建序列。

此外,获取下一个值的语法在 Postgres 中不是 sequence.nextval 而是 nextval('sequence')

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