MyBatis(四)高级映射
1.数据库字段名与实体类属性名不一致
当数据库中的字段名和对应的JavaBean中的属性名称不一样时,那么就无法映射,如何解决这个问题呢?
可以在Mapper.xml中书写sql语句时,再指定数据库字段名和JavaBean属性的对应关系,这样mybatis就知道哪个属性对应哪个字段,就不会出错了。
假设数据库字段名为 pid 和 pname,而 Products.java中的属性名为 id 和 name:
那么查询到的数据,mybatis就无法映射到实体类相应的字段上(因为名字不一样,mybatis也不知道该怎么映射了);
此时就需要我们手动的告诉mybatis,你要把哪个字段的数据映射到实体类的哪个属性上:
<!--在resultMap中指定是哪个类,以及该映射的id-->
<!--主键字段使用id标签映射,column为数据库的字段名,property为实体类的属性名-->
<!--非主键字段使用result标签映射-->
<resultMap id="products_map" type="products">
<id column="pid" property="id"/>
<result column="pname" property="name"/>
</resultMap>
<!--在需要映射的地方使用resultMap,指定要映射关系的id-->
<!--resultType和resultMap二选一即可-->
<select id="selectById" parameterType="int" resultMap="products_map">
select * from products where pid = #{pid};
</select>
但是在一开始的时候,最好就让数据库的字段名和实体类的属性名可以一一对应上!这样就减少了这些麻烦事情了。
2.联表查询
先赖准备两张表:
user表:
DROP TABLE IF EXISTS `kuser`;
CREATE TABLE `user` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`username` VARCHAR(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '用户名称',
`birthday` DATE NULL DEFAULT NULL COMMENT '生日',
`sex` CHAR(1) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '性别',
`address` VARCHAR(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '地址',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = INNODB AUTO_INCREMENT = 27 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC;
INSERT INTO `user` VALUES (1, '王五', NULL, '2', '山西太原');
INSERT INTO `user` VALUES (2, '张三', '2014-07-10', '1', '北京市');
INSERT INTO `user` VALUES (3, '张小明', NULL, '1', '山东聊城');
INSERT INTO `user` VALUES (4, '陈小明', NULL, '1', '河南郑州');
INSERT INTO `user` VALUES (5, '张三丰', '1976-06-01', '1', '河南郑州');
INSERT INTO `user` VALUES (6, '孙二娘', '2000-05-09', '2', '山东郓城');
INSERT INTO `user` VALUES (7, '武松', '1990-12-01', '1', '河北清河县');
orders表:
DROP TABLE IF EXISTS `orders`;
CREATE TABLE `orders` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`user_id` INT(11) NOT NULL COMMENT '下单用户id',
`number` VARCHAR(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '订单号',
`createtime` DATETIME(0) NOT NULL COMMENT '创建订单时间',
`note` VARCHAR(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = INNODB AUTO_INCREMENT = 11 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC;
INSERT INTO `orders` VALUES (6, 1, '1000010', '2019-12-04 13:22:35', NULL);
INSERT INTO `orders` VALUES (7, 1, '1000011', '2019-12-03 13:22:41', NULL);
INSERT INTO `orders` VALUES (8, 2, '1000012', '2019-12-12 16:13:23', NULL);
INSERT INTO `orders` VALUES (9, 2, '1000013', '2019-10-09 18:11:03', NULL);
INSERT INTO `orders` VALUES (10, 3, '1000014', '2019-08-22 08:22:11', NULL);
其中orders表中的user_id字段是user表中id字段的外键。
(1)一对一
一个订单只对应一个用户,为一对一的关系。
(创建user表和order表的实体类,别忘记在Order表中添加User属性)
此时有一个需求:查询订单编号为6的订单信息以及用户名称和地址
sql语句:
SELECT u.username,u.address,o.* FROM orders o JOIN `user` u ON o.`user_id` = u.`id` WHERE o.`id` = 6;
此时如果使用该sql语句直接查询的话,我们看看是什么结果:
<select id="selectByOrderId" parameterType="int" resultType="orders">
SELECT u.username,u.address,o.* FROM orders o JOIN `user` u ON o.`user_id` = u.`id` WHERE o.`id` = 6;
</select>
结果:
Orders{id=6, user_id=1, number='1000010', createtime=Wed Dec 04 13:22:35 CST 2019, note='null', user=null}
可以看到user的值为null。
这时因为mybatis需要我们指定关联的表:
<resultMap id="orders_resultMap" type="orders">
<!-- orders表映射-->
<id column="id" property="id"/>
<result column="user_id" property="user_id"/>
<result column="number" property="number"/>
<result column="createtime" property="createtime"/>
<result column="note" property="note"/>
<!-- 关联的user表映射-->
<association property="user" javaType="user">
<id column="id" property="id"/>
<result column="username" property="username"/>
<result column="birthday" property="birthday"/>
<result column="sex" property="sex"/>
<result column="address" property="address"/>
</association>
</resultMap>
<!-- resultMap使用上述的映射-->
<select id="selectByOrderId" parameterType="int" resultMap="orders_resultMap">
SELECT u.username,u.address,o.* FROM orders o JOIN `user` u ON o.`user_id` = u.`id` WHERE o.`id` = 6;
</select>
此时结果:
Orders{id=6, user_id=1, number='1000010', createtime=Wed Dec 04 13:22:35 CST 2019, note='null', user=User{id=6, username='王五', birthday=null, sex= , address='山西太原', orders=null}}
可以看到,user的内容已经被查出来了。
如果需求修改为:查询指定订单号的订单信息和用户信息。
sql语句:
SELECT u.*,o.* FROM orders o JOIN `user` u ON o.`user_id` = u.`id` WHERE o.`id` = 6;
我们使用mybatis执行,查看结果:
Orders{id=1, user_id=1, number='1000010', createtime=Wed Dec 04 13:22:35 CST 2019, note='null', user=User{id=1, username='王五', birthday=null, sex=2, address='山西太原'}}
会发现结果是有问题的,orders的id明明为6,结果却为1。
这是因为orders表和user表中都有id字段,mybatis又区分不出来了,但是修改表字段名的不可能的。
此时就需要我们为两张表相同字段取别名了:
并且对应的映射关系也需要修改了:
<resultMap id="orders_resultMap" type="orders">
<!-- orders表映射-->
<id column="oid" property="id"/>
<!-- 关联的user表映射-->
<association property="user" javaType="user">
<id column="uid" property="id"/>
</association>
</resultMap>
<!-- resultMap使用上述的映射-->
<select id="selectByOrderId" parameterType="int" resultMap="orders_resultMap">
SELECT u.*,o.*,u.id uid,o.id oid FROM orders o JOIN `user` u ON o.`user_id` = u.`id` WHERE o.`id` = 6;
</select>
此时的结果:
Orders{id=6, user_id=0, number='null', createtime=null, note='null', user=User{id=1, username='null', birthday=null, sex= , address='null'}}
此时orders表的id就变为正确的6了。
(2)一对多映射
一个用户对应多个订单,即一对多的关系。
需求:查询指定用户对应的用户信息和订单信息;
(还是上述所提到的两个实体类,别忘记了在User类中添加List
此时也需要关联查询:(一对一的关联查询使用association标签,而一对多的关联查询使用collection标签)
sql语句:
SELECT u.*,o.* FROM orders o JOIN `user` u ON o.`user_id` = u.`id` WHERE u.`id` = 1;
mybatis:
一对多映射必须存在一个手动映射关系。就算是数据库字段名和实体类属性一直,也必须存在一个手动映射,并且剩下的字段如果不手动映射,需要设置autoMapping="true" ,将自动映射字段名与属性名能对应的字段。
<!-- 使用autoMapping=true,自动映射-->
<resultMap id="user_resultMap" type="user" autoMapping="true">
<id column="uid" property="id"/>
<!-- 注意这里为ofType-->
<collection property="orders" ofType="orders" autoMapping="true">
<id column="oid" property="id"/>
</collection>
</resultMap>
<select id="selectByUserId" parameterType="int" resultMap="user_resultMap">
SELECT u.*,o.*,u.id uid,o.id oid FROM orders o JOIN `user` u ON o.`user_id` = u.`id` WHERE u.`id` = 1;
</select>
结果:
User{id=1, username='王五', birthday=null, sex=2, address='山西太原', orders=[Orders{id=6, user_id=1, number='1000010', createtime=Wed Dec 04 13:22:35 CST 2019, note='null'}, Orders{id=7, user_id=1, number='1000011', createtime=Tue Dec 03 13:22:41 CST 2019, note='null'}]}
(3)嵌套查询
在sql语句中,除了联表查询,我们还可以使用子查询,那么在mybatis中如何实现子查询呢?
sql语句:
SELECT * FROM USER WHERE id = (SELECT user_id FROM orders WHERE id = 6);
mybatis:
<!-- 嵌套查询-->
<resultMap id="order_rm" type="orders" autoMapping="true">
<id column="id" property="id"/>
<!-- column为传给内层查询的参数-->
<association property="user" column="user_id" select="selectUser" javaType="user"/>
</resultMap>
<!-- 外层查询,即()里的-->
<select id="selectOrder" parameterType="int" resultMap="order_rm">
select * from orders where id = #{id}
</select>
<!-- 内层查询-->
<select id="selectUser" parameterType="int" resultType="user">
select * from user where id = #{id}
</select>