spring 数据 JPA - mysql - findById() 为空,除非之前调用 findAll()

nag*_*hun 9 mysql spring spring-data spring-data-jpa

我正在为这个奇怪的错误而苦苦挣扎: a 的findById()方法CrudRepository返回Optional.empty,除非findAll()在使用 mysql 之前被调用。

例如

用户

@Entity
public class User {

    @Id
    @GeneratedValue
    private UUID id;

    public UUID getId() {
        return id;
    }

}
Run Code Online (Sandbox Code Playgroud)

用户库

public interface UserRepository extends CrudRepository<User, UUID> { }
Run Code Online (Sandbox Code Playgroud)

用户服务

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    @Transactional
    public UUID create() {
        final User user = new User();
        userRepository.save(user);
        return user.getId();
    }

    @Transactional
    public User find(@PathVariable UUID userId) {
        // userRepository.findAll(); TODO without this functin call, Optinoal.empty is returned by the repo
        return userRepository.findById(userId).orElseThrow(() -> new IllegalArgumentException(String.format("missing user:%s", userId)));
    }
}
Run Code Online (Sandbox Code Playgroud)

用户应用程序

@SpringBootApplication
public class UserApp {

    private static final Logger LOG = LoggerFactory.getLogger(UserApp.class);

    @Autowired
    private UserService userService;

    @EventListener
    public void onApplicationEvent(ContextRefreshedEvent event) {
        final UUID userId = userService.create();
        final User user = userService.find(userId);
        LOG.info("found user: {}", user.getId());
    }

    public static void main(String[] args) {
        SpringApplication.run(UserApp.class, args);
    }

}
Run Code Online (Sandbox Code Playgroud)

应用程序属性

spring.datasource.url=jdbc:mysql://localhost:3306/db_test
spring.datasource.username=springuser
spring.datasource.password=ThePassword

spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.show-sql=true
spring.jpa.database=mysql
Run Code Online (Sandbox Code Playgroud)

为什么findAll()方法调用会改变 的结果findById()

编辑:休眠日志findAll

Hibernate: drop table if exists user
Hibernate: create table user (id binary(255) not null, primary key (id)) engine=MyISAM
Hibernate: insert into user (id) values (?)
Hibernate: select user0_.id as id1_0_ from user user0_
Run Code Online (Sandbox Code Playgroud)

没有:

Hibernate: drop table if exists user
Hibernate: create table user (id binary(255) not null, primary key (id)) engine=MyISAM
Hibernate: insert into user (id) values (?)
Hibernate: select user0_.id as id1_0_0_ from user user0_ where user0_.id=?
Run Code Online (Sandbox Code Playgroud)

小智 6

我面临着同样的问题。根本原因是不可空的 @ManyToOne 关系与表中持久化的数据之间不匹配。我有这个:

@ManyToOne(optional = false)
  @JoinColumn(name="batch_id")
  private Batch batch;
Run Code Online (Sandbox Code Playgroud)

这意味着任何行中的batch_id 都不能为空。但是,我的行的batch_id 外键具有空值。删除Optional = false(这是预期的业务规则)后,findById开始按预期工作。

从这个线程得到指示:我厌倦了用 JpaRepository 做一些事情但无法使用 findById 找到行,

  • 对我来说,这是同样的根本原因!我有一个“@JoinColumn(name = "voucher_id", nullable = false)”,但保留了“voucher_id”列的“null”值的实体。删除非空约束后,一切又按预期工作了! (2认同)

小智 5

正如之前(但更笼统地)在一些测试之后已经写过的那样,我的结论是当@ManyToOne 关系中的必填字段和数据库数据存在差异时会出现问题。例如,假设有一个没有必填字段约束的表,但同时你的 Hibernate 模型类带有 @ManyToOne(optional = false) 约束。并想象填充表(例如使用 SQL 脚本),将被指示的字段保留为null。当您尝试执行findById()deleteById或任何暗示通过 Id 获取的操作时,您将遇到这种(可怕的)副作用。


yus*_*suf 2

尝试使用下面的代码;

@Id
@GeneratedValue(generator = "uuid2")
@GenericGenerator(name = "uuid2", strategy = "uuid2")
@Type(type="uuid-char")
private UUID id;
Run Code Online (Sandbox Code Playgroud)