如何使用Spring Data JDBC插入带有自定义ID的记录?

11

在Spring Data JPA中,我可以使用@GeneratedValue(strategy = GenerationType.AUTO)来插入一条带有自定义id的记录,但是对于Spring Data JDBC,如何插入具有自定义id的记录?我已经尝试使用id插入,但是没有抛出异常,并且记录没有插入到表中。

4个回答

18
使用Spring Data JDBC实现此操作的方式是注册一个BeforeSaveEvent ApplicationListener,该监听器创建ID并将其设置在实体中。
@Bean
public ApplicationListener<BeforeSaveEvent> idSetting() {

    return event -> {

        if (event.getEntity() instanceof LegoSet) {

            LegoSet legoSet = (LegoSet) event.getEntity();
            if (legoSet.getId() == null) {
                legoSet.setId(createNewId());
            }
        }
    };
}

Spring Data Examples中有一个示例,展示了以下情况:

你尝试向表中插入一行记录,但是既没有插入成功,也没有抛出异常的原因是:Spring Data JDBC认为实体已经存在,因为ID已设置,并执行了更新操作。 但由于它并不存在,所以更新失败,没有任何操作发生。 值得注意的是,可以创建改进请求来检查更新计数是否为0。

更新

自版本1.1起,JdbcAggregateTemplate.insert可用于允许您进行插入而无需检查聚合是否为新聚合。 如果需要,您可以在存储库中创建自定义方法,或者可以在需要时直接将模板自动装配并直接使用它。

此外,通过DATAJDBC-438,Spring Data JDBC将抛出异常,如果保存了一个聚合并导致更新但更新零行,则此类问题不会被忽略。


谢谢您的回答,我也同意您的解释,虽然这种方法不是很好,但它可以解决我的问题,谢谢! - harrell hao
1
我有同样的问题,即使它能够运行,这种解决方案也非常复杂。许多情况下,id已经由数据模型定义好了,不需要随机创建。像article number之类的东西已经很适合作为id了,所以我不想要一个随机的id被创建,因为那样的话我就必须要确保没有重复的文章。这个事件监听器似乎更像是一个变通方法。你们计划直接在spring-data-jdbc中包含upsert这样的功能吗? - Erik P
@ErikP 当然可以。请创建一个工单:https://jira.spring.io/projects/DATAJDBC - Jens Schauder
1
@ErikP 我刚刚合并了 jira.spring.io/browse/DATAJDBC-282,它提供了一个 insert 方法,允许您预填充 id。我想这可能会有所帮助。 - Jens Schauder
JdbcAggregateTemplate 非常值得注意! - Torsten Ojaperv
这个解决方案似乎更像是一种变通方法。我更喜欢通过数据库生成ID或实现Persistable接口,而不是订阅此事件。这个解决方案不够透明。 - Stanislav Machel

5
我找到了另一种解决这个问题的方法,虽然有点麻烦。
// BaseEntity
public class BaseEntity implements Persistable, Serializable {

    @Id
    String id;

    @Transient
    @JsonIgnore
    private boolean newEntity;

    @Override
    public Object getId() {
        return id;
    }

    public void setNew(boolean newInstance) {
        this.newEntity = newInstance;
    }

    @Override
    @JsonIgnore
    public boolean isNew() {
        return newEntity;
    }
}

// User
User extends BaseEntity
...


// insert
User user = new User();
user.id = "5bffb39cc5e30ba067e86dff";
user.setName("xiangzi");
user.setNew(true);
userRepository.save(user);


// update
User user = new User();
user.id = "5bffb39cc5e30ba067e86dff";
user.setName("xiangzi2");
userRepository.save(user);

当需要添加一行时,插入需加上代码user.setNew(true);

谢谢!

我还在此处添加了一条评论


0

我参考了上面Xianghi的答案,并稍微不同地使用了它:

@Data
@Table
public class TestDataTable implements Persistable<String> {


@Id
String id;

private String name;

@Transient
@JsonIgnore
private boolean newEntity;

@Override
public String getId() {
    return id;
}

public void setNew(boolean newInstance) {
    this.newEntity = newInstance;
}

@Override
@JsonIgnore
public boolean isNew() {
    return newEntity;
}
}

// created new record when we use task.setNew(true) in TestDataTable with String Id.
public Mono<TestDataTable> saveTestData(TestDataTable task) {
    task.setNew(true);

    return testRepository.save(task);
}

0

我也遇到过这个限制。有一些解决方法https://spring.io/blog/2021/09/09/spring-data-jdbc-how-to-use-custom-id-generation,但对我来说并不是完美的解决方案。

我正在为SQLite开发Spring Data JDBC集成,它提供了一个仓库助手类,公开了JdbcAggregateTemplate的insert()update() API(https://github.com/komamitsu/spring-data-sqlite#repository-helper-class)。该机制并非特定于SQLite,因此我认为同样的方法也可以适用,尽管需要一些实现。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接