Spring Boot JPA 复合主键只查询部分主键

来源:互联网 发布:ups 寿命 知乎 编辑:程序博客网 时间:2024/05/29 12:36

Spring Data JPA给了我们很强大的功能,我们只需要通过编写一个继承自JpaRepository的接口就能完成数据访问。最近使用过程中,碰到一个问题:

数据库的表中有多个主键,我们和数据库交互的实体(Entity)中只定义了其中部分主键,也就是数据库表中的字段多于Entity中定义的字段。当我们查询时发现,查询返回的结果和我们预想的完全不一致。

接下来我们来逐步验证并进行完善:

  • 首先是建表,我们采用的是MySql数据库:
SET FOREIGN_KEY_CHECKS=0;-- ------------------------------ Table structure for `order_info`-- ----------------------------DROP TABLE IF EXISTS `order_info`;CREATE TABLE `order_info` (  `id` varchar(20) NOT NULL DEFAULT '',  `time` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',  `flag` varchar(1) DEFAULT NULL,  `memo` varchar(100) CHARACTER SET utf8 DEFAULT NULL,  PRIMARY KEY (`id`,`time`)) ENGINE=InnoDB DEFAULT CHARSET=latin1;-- ------------------------------ Records of order_info-- ----------------------------INSERT INTO `order_info` VALUES ('1', '2017-07-12 10:18:35', '0', '1号订单');INSERT INTO `order_info` VALUES ('1', '2017-07-12 10:19:21', '1', '1号订单子订单1');INSERT INTO `order_info` VALUES ('1', '2017-07-12 10:19:59', '2', '1号订单子订单2');INSERT INTO `order_info` VALUES ('2', '2017-07-12 10:19:14', '0', '2号订单');INSERT INTO `order_info` VALUES ('3', '2017-07-12 10:19:17', '0', '3号订单');

设置了订单号(id)下单时间(time)两个字段作为主键。

  • Entity定义上,我们只引用了订单号(id)这一个主键
@Entity@Table(name = "order_info")public class OrderEntity implements Serializable {    @Id    private String id;    private String flag;    private String memo;    //省略get、set语句…}
  • 编写调用接口,根据订单编号获取订单信息
@RestController@RequestMapping(value = "/order")public class OrderController {    @Autowired    private OrderService orderService;    @RequestMapping(value ="/get/{id}")    @ResponseBody    public List<OrderEntity> getOrder(@PathVariable("id") String id) {        return orderService.getById(id);    }}
  • 启动工程后访问接口,发现返回的值和我们预期的值不一样,返回的3条记录竟然是一样的!!!
    订单信息相同

查看下Hibernate的运行日志:

Hibernate: select orderentit0_.id as id1_0_, orderentit0_.flag as flag2_0_, orderentit0_.memo as memo3_0_ from order_info orderentit0_ where orderentit0_.id=?

补全id后我们手动运行sql发现运行结果和数据库一致,并不是JPA返回给我们的结果。
这里写图片描述

  • 分析下来,发现是JPA构建OrderEntity实体类时判断主键仅为id,将查询结果转换为实体时默认取第一条数据进行反序列化,导致返回的三条数据都是默认数据库返回结果中id=1的第一条数据。

对于这种情况,我们可以通过添加@IdClass注解的方式来完善我们的查询结果。@IdClass故名思意说明主键是一个集合类,类包含的全部字段均为主键。

所以我们目前有两种方案可以实施:

  1. 新建一个类,添加@IdClass注解,将表结构的全部主键都引入(id,time);
  2. 在现有类上添加@IdClass,将我们查询的所有字段都标记为主键。

这里我们选择了方案2。

@Entity@Table(name = "order_info")@IdClass(OrderEntity.class)public class OrderEntity implements Serializable {    @Id    private String id;    private String flag;    private String memo;    //省略get、set语句…}

添加@IdClass(OrderEntity.class)后,JPA认为我们实体类中所有字段均为主键,将查询结果反序列化的时候会判定全部字段来取出,而不仅仅判定id就取出。我们看下执行的结果:
这里写图片描述
添加后,我们得到的结果和预期的一致了。小伙伴们有需求的话,也可以自行尝试下第一种方案。


最后附上源码下载链接。

https://github.com/Shallow-Xu/union_primary_key

原创粉丝点击