【问题标题】:Spring JPA findByDomainClass generates wrong where clauseSpring JPA findByDomainClass 生成错误的 where 子句
【发布时间】:2016-01-02 17:47:03
【问题描述】:

我在使用 Spring JPA 时遇到了一个问题。我用谷歌搜索并研究了半天,但没有结果。让我告诉你我的代码。我编写了一个实体,它在数据库中引用另一个具有外键的域类,但实体的连接列不是主键“id”。两个实体如下:

@Entity
@Table(name = "tb_user")
public class UserEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String username;
    ... getters and setters ...
}

@Entity
@Table(name = "tb_timebank")
public class TimebankEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    @ManyToOne
    @JoinColumn(name = "username")
    private UserEntity user;
    ... getters and setters ...
}

我使用 spring JPA CrudRepository 来做自定义查询:

@Repository
public interface UserRepository extends CrudRepository<UserEntity, Long> {
    public UserEntity findByUsername(String username);
}

@Repository
public interface TimebankRepository extends CrudRepository<TimebankEntity, Long> {
    public TimebankEntity findByUser(UserEntity user);
}

这很简单。但是,当我在服务中执行业务逻辑时,出现了问题。

@Service
public class TimebankService {
    @Autowired
    TimebankRepository timebankRepository;

    @Autowired
    UserRepository userRepository;
    public TimebankDto getTimebank(String username) {
        UserEntity userEntity = userRepository.findByUsername(username);
        TimebankEntity timebankEntity = timebankRepository
                .findByUser(userEntity);
        return new TimebankDto(timebankEntity.getUser().getUsername(),
                timebankEntity.getLeaveType().getCode(),
                timebankEntity.getTotalHours(),
                timebankEntity.getAvailableHours());
    }
}

我使用用户名“Tom”运行上面的代码,我可以获得数据库中存在的正确 userEntity。但是 findByUser(userEntity) 返回 null 结果。 hibernate生成的sql和message输出如下:

Hibernate: select userentity0_.id as id1_2_, userentity0_.introduction as introduc2_2_, userentity0_.password as password3_2_, userentity0_.username as username4_2_ from tb_user userentity0_ where userentity0_.username=?
Hibernate: select timebanken0_.id as id1_1_, timebanken0_.available_hours as availabl2_1_, timebanken0_.leave_type as leave_ty4_1_, timebanken0_.total_hours as total_ho3_1_, timebanken0_.username as username5_1_ from tb_timebank timebanken0_ left outer join tb_user userentity1_ on timebanken0_.username=userentity1_.id where userentity1_.id=?
o.h.engine.jdbc.spi.SqlExceptionHelper:SQL Warning Code: 1292, SQLState: 22007
o.h.engine.jdbc.spi.SqlExceptionHelper:Truncated incorrect DOUBLE value: 'Tom'

似乎“Tom”被分配给“id”字段,所以它返回一个空结果,但为什么会发生这种情况?我的代码有什么问题?我检查了 spring jpa 参考,但没有找到答案。请给我一些建议,并提前感谢!

【问题讨论】:

  • 只是好奇:你为什么用非 Id 字段@JoinColumn?按id加入不是更有用吗?
  • 数据库中的索引键是外键tb_timebank(username) references tb_user(username),主键tb_usertb_timebank都是id,两个id没有外键约束。
  • @FlorianSchaetz 感谢您的快速回复。有2个原因。首先,我在数据库中设置了一个外键。其次,我可以直接从 tb_timebank 获取用户名。正如你所说,我将@JoinColumn 注释替换为@JoinColumn(insertable=false,updatable=false,referencedColumnName = "id"),然后我收到另一条消息:`'字段列表'中的未知列'timebanken0_.user_id'`。

标签: java spring hibernate spring-mvc jpa


【解决方案1】:

首先,您看错了地方。您在这里遇到了 JPA 映射问题,并且您的 JPA 实现是 Hibernate。因此,您应该在 JPA/Hibernate 文档中查找问题,而不是在 Spring 文档中。

您已经告诉 JPA/Hibernate TimebankEntity 和 UserEntity 之间存在关联:

@ManyToOne
@JoinColumn(name = "username")
private UserEntity user;

这意味着在表tb_timebank 中有一个列username,用于引用tb_user 表的一行。但是您不会在任何地方说此列引用了​​tb_user 表的哪一列。

唯一引用另一个表的一行的最自然的方法是什么?当然是主键。因此,当然,如果您不指定任何内容,那么这就是 JPA 将使用的内容。怎么改?

让我们阅读API documentation of JoinColumn

引用的列名

public abstract java.lang.String referencedColumnName

(可选)此外键列引用的列的名称。

[...]

默认(仅在使用单个连接列时适用):与引用表的主键列同名。

所以,默认情况下,被引用的列名是被引用表的主键的列名,即tb_user.id

要引用另一列,您需要为此属性指定一个值:

@JoinColumn(name = "username", referencedColumnName = "username")

也就是说,你最好使用默认值:这样会更有效率,不会在两个用户同名的情况下造成问题,并且不会在用户更改他的名字时强制你更新tb_timebank名字。

【讨论】:

  • 感谢您的详细回答。我尝试替换您在上面给出的@JoinColumn,现在我得到了生成的 sql:Hibernate: select timebanken0_.id as id1_1_, timebanken0_.available_hours as availabl2_1_, timebanken0_.leave_type as leave_ty4_1_, timebanken0_.total_hours as total_ho3_1_, timebanken0_.username as username5_1_ from tb_timebank timebanken0_ left outer join tb_user userentity1_ on timebanken0_.username=userentity1_.username where userentity1_.id is null。另一方面,我实际上已经尝试过,但它不起作用。
  • 你从什么得到这个SQL?您调用了哪个存储库方法,使用了哪个参数?
  • @Nizet: 我只是用@JoinColumn(name="username",referencedColumnName = "username") 替换我的@JoinColumn 并在上面的服务类中运行findByUser(userEntity) 并使用属性'spring.jpa.show-sql= 从控制台获取该sql真的'。
  • 我猜你用一个带有空 ID 的 userEntity 实例调用了该方法。所以,生成的查询是绝对正确的。您要求提供与 ID 为 null 标识的用户相关联的 TimeBank,这就是查询的作用。将实体传递给查询相当于按其 ID 进行搜索。 JPA 查询不是“示例查询”。要按用户名搜索,您需要一个 findByUserUsername(String userName) 方法。或者更好的是,任何带有 @Query("select tb from Timebank tb where tb.user.username = :userName") 注释的方法
  • @Nizet:首先我调试了一下,发现findByUser()方法中传入的user参数有一个非空id。 findByUsername() 工作正常。问题出在findByUser() 方法上。然后我尝试了你的 @Query 注释,它得到了正常的结果。我很困惑。我的代码中findByUser() 的用法错了吗?
猜你喜欢
  • 2022-12-02
  • 2011-01-27
  • 2020-01-04
  • 2017-01-18
  • 2021-12-24
  • 2016-04-29
  • 2015-09-20
  • 2010-11-30
  • 1970-01-01
相关资源
最近更新 更多