rvi*_*t34 2 java postgresql liquibase spring-data-jpa spring-boot
我正在使用 liquibase 作为我当前项目的迁移工具。
问题是我正在尝试使用自定义序列为实体自动生成 id,但发生了错误。
我定义了一个实体表和自定义序列,如下所示:
<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>
Run Code Online (Sandbox Code Playgroud)
但是当应用此迁移时,我在 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
);
Run Code Online (Sandbox Code Playgroud)
对我来说,第一件不可预知的事情是为什么 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> {}
Run Code Online (Sandbox Code Playgroud)
测试
@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));
}
}
Run Code Online (Sandbox Code Playgroud)
在测试日志中我看到:
Hibernate: select nextval ('seq_orders')
...
java.lang.AssertionError:
Expected: is <Order(id=50, number=order-n-01, ...>
but: was null
Run Code Online (Sandbox Code Playgroud)
测试完成后在数据库中我看到三个订单,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 Boot 测试配置:
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
Run Code Online (Sandbox Code Playgroud)
小智 5
如果您指定serial为数据类型,则不应指定任何默认值,因为 Postgres已经为您执行此操作。序列 和orders_id_seq默认值是在您使用Postgres时自动创建的,而不是serial由Liquibase 创建的。
因此,如果您想使用自己的序列(无论出于何种原因),请将列指定为integer并指定defaultValueComputedusing nextval()。您还需要在创建表之前创建序列。
另外,获取下一个值的语法不是sequence.nextvalPostgresnextval('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>
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
9551 次 |
| 最近记录: |