英文:
Spring Boot + Spring Data JPA + Sybase: No keys were automatically generated Sybase IDENTITY column
问题
I understand your request. Here is the translated content:
我正在将一个遗留应用迁移到一个新的Apache Camel Spring Boot应用程序,在这个应用程序中我使用了Spring Data JPA与Sybase数据库交互。
遗留应用程序使用旧的技术版本,如Spring 3.x, Hibernate 3.x, jconn3
等。
Sybase数据库中的所有表都定义了IDENTITY
列作为主键。
遗留应用程序使用以下方式定义身份列和Block
与Allocation
实体之间的关系(多对一双向
):
Block.hbm.xml
<id name="blockPk" type="java.lang.Long">
<column name="block_pk"/>
<generator class="native"/>
</id>
<set name="allocations" inverse="true" cascade="all,delete-orphan" table="allocations" order-by="allocation_pk asc" fetch="select">
<key>
<column name="block_pk" not-null="true"/>
</key>
<one-to-many class="Allocation"/>
</set>
Allocation.hbm.xml
<id name="allocationPk" type="java.lang.Long">
<column name="allocation_pk"/>
<generator class="native"/>
</id>
<many-to-one name="block" class="Block" type="select">
<column name="block_pk" not-null="true"/>
</many-to-one>
在新应用程序中,我做了类似这样的事情:
Block.java
@Id
@Column(name = "block_pk")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long blockPk;
@OneToMany(mappedBy = "block", cascade = CascadeType.ALL, orphanRemoval = true)
@OrderBy("allocationPk")
@Fetch(FetchMode.SELECT)
@ToString.Exclude
private Set<Allocation> allocations;
Allocation.java
@Id
@Column(name = "allocation_pk")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long allocationPk;
@ManyToOne
@JoinColumn(name = "block_pk", nullable = false)
@Fetch(FetchMode.SELECT)
@ToString.Exclude
private Block block;
Repositories
@Repository
public interface BlockRepository extends JpaRepository<Block, Long> {
}
@Repository
public interface AllocationRepository extends JpaRepository<Allocation, Long> {
}
DbStore.java
@Component
public class DbStore {
@Autowired
private BlockRepository blockRepository;
@Autowired
private AllocationRepository allocationRepository;
public Block save(Block block) {
return blockRepository.save(block);
}
public Allocation save(Allocation allocation) {
return allocationRepository.save(allocation);
}
}
AllocationHandler.java
public List<Allocation> handleRequest(String input) {
Block block = new Block();
block.setQuantity(new BigDecimal(1000000));
// Persist Block
LOGGER.info("Persist block in DB");
block = dbStore.save(block);
LOGGER.info("Persisted Block in DB with Id [{}]", block.getBlockPk());
Allocation allocation = new Allocation();
allocation.setQuantity(new BigDecimal(500000));
block.addAllocation(allocation);
allocation.setBlock(block);
LOGGER.info("Persist allocation in DB");
allocation = dbStore.save(allocation);
LOGGER.info("Persisted allocation in DB with Id [{}]", allocation.getAllocationPk());
LOGGER.info("Update Block");
block = dbStore.save(block);
return Arrays.asList(allocation);
}
在新应用程序中,我遇到了两个问题:
- 当我尝试像
AllocationHandler
类中所示那样持久化Block
和Allocation
时,Block
被持久化为一个唯一的自动生成的ID
,这是可以的。但是相同的ID也被用于持久化Allocation
,而不是根据身份列中的最新值生成不同的自动生成的ID。我确信Sybase数据库和我的SybDriver版本(即jconnect-7.0.GA)没有问题,因为在使用较低版本驱动程序的遗留应用程序中,它已经按照预期工作。 - 当我从
Allocation
中删除了对Block
的依赖,尝试只持久化Allocation
时,我得到了以下异常:"生成的键不可用,因为要么使用了Statement.NO_GENERATED_KEYS,要么没有在Sybase中自动生成键。"我不确定为什么在步骤1中对Block
有效,但在Allocation
中不起作用。
请有人检查并提供帮助。
英文:
I am migrating one legacy application to a newer Apache Camel Spring Boot application where I am using Spring Data JPA to interact with Sybase database.
Legacy application uses older technologies versions like Spring 3.x, Hibernate 3.x, jconn3
etc.
All the Tables in the Sybase DB have primary key defined as IDENTITY
column.
Legacy application uses following to define identity columns and relation between Block
and Allocation
entity (many-to-one bidirectional
):
Block.hbm.xml
<id name="blockPk" type="java.lang.Long">
<column name="block_pk"/>
<generator class="native"/>
</id>
<set name="allocations" inverse="true" cascade="all,delete-orphan" table="allocations" order-by="allocation_pk asc" fetch="select">
<key>
<column name="block_pk" not-null="true"/>
</key>
<one-to-many class="Allocation"/>
</set>
Allocation.hbm.xml
<id name="allocationPk" type="java.lang.Long">
<column name="allocation_pk"/>
<generator class="native"/>
</id>
<many-to-one name="block" class="Block" type="select">
<column name="block_pk" not-null="true"/>
</many-to-one>
In new application I am doing something like this:
Block.java
@Id
@Column(name = "block_pk")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long blockPk;
@OneToMany(mappedBy = "block", cascade = CascadeType.ALL, orphanRemoval = true)
@OrderBy("allocationPk")
@Fetch(FetchMode.SELECT)
@ToString.Exclude
private Set<Allocation> allocations;
Allocation.java
@Id
@Column(name = "allocation_pk")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long allocationPk;
@ManyToOne
@JoinColumn(name = "block_pk", nullable = false)
@Fetch(FetchMode.SELECT)
@ToString.Exclude
private Block block;
Repositories
@Repository
public interface BlockRepository extends JpaRepository<Block, Long> {
}
@Repository
public interface AllocationRepository extends JpaRepository<Allocation, Long> {
}
DbStore.java
@Component
public class DbStore {
@Autowired
private BlockRepository blockRepository;
@Autowired
private AllocationRepository allocationRepository;
public Block save(Block block) {
return blockRepository.save(block);
}
public Allocation save(Allocation allocation) {
return allocationRepository.save(allocation);
}
}
AllocationHandler.java
public List<Allocation> handleRequest(String input) {
Block block = new Block();
block.setQuantity(new BigDecimal(1000000));
// Persist Block
LOGGER.info("Persist block in DB");
block = dbStore.save(block);
LOGGER.info("Persisted Block in DB with Id [{}]", block.getBlockPk());
Allocation allocation = new Allocation();
allocation.setQuantity(new BigDecimal(500000));
block.addAllocation(allocation);
allocation.setBlock(block);
LOGGER.info("Persist allocation in DB");
allocation = dbStore.save(allocation);
LOGGER.info("Persisted allocation in DB with Id [{}]", allocation.getAllocationPk());
LOGGER.info("Update Block");
block = dbStore.save(block);
return Arrays.asList(allocation);
}
There are two issues I am facing in the new application:
- When I try to persist
Block
and thenAllocation
as shown inAllocationHandler
class,Block
gets persisted with aunique auto-generated id
which is fine. But the same id is being used to persistAllocation
as well instead of having a different auto-generated id based on the latest value in identity columns. I am sure there is no issue with Sybase DB and my version of SybDriver i.e. jconnect-7.0.GA because it already works as per the expectation in legacy application which uses lower version of this driver. - When I removed the dependency of
Block
fromAllocation
and tried to persist only theAllocation
, I got following exception: "Generated keys are not available because either the Statement.NO_GENERATED_KEYS was used or no keys were automatically generated Sybase.". I am not sure why it worked forBlock
in step 1 but not working forAllocation
.
Could someone please check and help here?
答案1
得分: 1
终于,我成功解决了这个问题。
问题出在我试图持久化的几个字段上。其中一个BigDecimal字段存在精度问题,另一个是时间戳字段,其格式不符合列的预期。
为Spring和Hibernate启用了TRACE模式的日志记录,这些日志非常具有误导性。
在成功和不成功的插入情况下,它生成了类似的语句。日志显示好像插入的数据没有问题,但Id列的自动生成逻辑有问题。
最终,我尝试手动构建插入查询,以便对数据库运行查询,显然失败了,并帮助我朝正确的方向前进。
英文:
So finally I was able to solve this issue.
Issue was with couple of fields that I was trying to persist. One of the BigDecimal fields had the precision issue and other was a timestamp field where format was not as per the column's expectation.
Logs were enabled in TRACE mode for spring as well as hibernate, which were very misleading.
In case of a successful as well as unsuccessful inserts, it was generating the similar kind of statements. Logs portrayed as if there was no issue with the data it is trying to insert and something wrong with Id column auto generation logic.
But finally, I constructed the insert query in an attempt to manually run the query against DB which obviously failed and helped me go in the right direction.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论