Spring Boot + Spring Data JPA + Sybase:未自动生成Sybase IDENTITY列的键。

huangapple go评论65阅读模式
英文:

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列作为主键。

遗留应用程序使用以下方式定义身份列和BlockAllocation实体之间的关系(多对一双向):

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

在新应用程序中,我遇到了两个问题:

  1. 当我尝试像AllocationHandler类中所示那样持久化BlockAllocation时,Block被持久化为一个唯一的自动生成的ID,这是可以的。但是相同的ID也被用于持久化Allocation,而不是根据身份列中的最新值生成不同的自动生成的ID。我确信Sybase数据库和我的SybDriver版本(即jconnect-7.0.GA)没有问题,因为在使用较低版本驱动程序的遗留应用程序中,它已经按照预期工作。
  2. 当我从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:

  1. When I try to persist Block and then Allocation as shown in AllocationHandler class, Block gets persisted with a unique auto-generated id which is fine. But the same id is being used to persist Allocation 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.
  2. When I removed the dependency of Block from Allocation and tried to persist only the Allocation, 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 for Block in step 1 but not working for Allocation.

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.

huangapple
  • 本文由 发表于 2023年4月20日 00:56:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/76057063.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定