mybatis基础2
1、输入与输出映射
Mapper.xml映射文件中定义了操作数据库的sql,每个sql是一个statement,映射文件是mybatis的核心。
1.1parameterType(输入类型)
1.1.1基本类型
1.1.2pojo类型
Mybatis使用ognl表达式解析对象字段的值,#{}或者${}括号中的值为pojo属性名称
1.1.3传递pojo包装对象
开发中通过pojo传递查询条件 ,查询条件是综合的查询条件,不仅包括用户查询条件还包括其它的查询条件
(比如将用户购买商品信息也作为查询条件),这时可以使用包装对象传递输入参数。
Pojo类中包含pojo。
QueryVo包装对象
public class QueryVo { private User user; public User getUser() { returnuser; } publicvoid setUser(User user) { this.user = user; } }
添加Mapper配置
<!-- 使用包装类型查询用户 使用ognl从对象中取属性值,如果是包装对象可以使用.操作符来取内容部的属性 --> <selectid="findUserByQueryVo"parameterType="queryvo"resultType="user"> SELECT * FROM user where username like '%${user.username}%' </select>
定义接口
List<User> findUserByQueryVo(QueryVo queryVo)
测试方法
@Test publicvoidtestFindUserByQueryVo() throws Exception { SqlSession sqlSession = sessionFactory.openSession(); //获得mapper的代理对象 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); //创建QueryVo对象 QueryVo queryVo = new QueryVo(); //创建user对象 User user = new User(); user.setUsername("刘"); queryVo.setUser(user); //根据queryvo查询用户 List<User> list = userMapper.findUserByQueryVo(queryVo); System.out.println(list); sqlSession.close(); }
1.2resultType(输出类型)
1.2.1输出基本类型
添加mapper配置
<!-- 只有返回一行一列的时候,那么返回值类型才可以是指定成基本类型 --> <select id="findUserCount" resultType="java.lang.Integer"> select count(*) from user </select>
Mapper接口
public int findUserCount(User user) throws Exception;
测试:
Publicvoid testFindUserCount() throws Exception{ //获取session SqlSession session = sqlSessionFactory.openSession(); //获取mapper接口实例 UserMapper userMapper = session.getMapper(UserMapper.class); User user = new User(); user.setUsername("管理员"); //传递Hashmap对象查询用户列表 intcount = userMapper.findUserCount(user); //关闭session session.close(); }
输出简单类型必须查询出来的结果集有一条记录,最终将第一个字段的值转换为输出类型。
使用session的selectOne可查询单条记录。
1.2.2输出pojo类型
配置Mapper
<select id="findUserByUserName" parameterType="java.lang.String" resultType="com.wang.pojo.User"> SELECT * from user where username like '%${value}%' </select>
Mapper接口
public List<User> findUserByUserName(String name);
测试
@Test public void findUserByUserName() throws Exception{ SqlSession session = factory.openSession(); //通过getMapper实例化接口 UserMapper userMapper = session.getMapper(UserMapper.class); List<User> users= userMapper.findUserByUserName("王"); System.err.println(users); }
2、动态sql
通过mybatis提供的各种标签方法实现动态拼接sql。
2.1 if
<!-- 传递pojo综合查询用户信息 --> <selectid="findUserList"parameterType="user"resultType="user"> select * from user where 1=1 <iftest="id!=null"> and id=#{id} </if> <iftest="username!=null and username!=''"> and username like '%${username}%' </if> </select>
注意要做不等于空字符串校验。
2.2 Where
<selectid="findUserList"parameterType="user"resultType="user"> select * from user <where> <iftest="id!=null and id!=''"> and id=#{id} </if> <iftest="username!=null and username!=''"> and username like '%${username}%' </if> </where> </select>
where 标签作用:
1、会自动向sql中添加where关键字
2、会自动去掉第一个条件的and关键字
2.3 foreach
向sql传递数组或List,mybatis使用foreach解析,如下
在QueryVo中声明:
public class QueryVo { private List<Integer> ids;public List<Integer> getIds() { return ids; } public void setIds(List<Integer> ids) { this.ids = ids; } }
测试:
@Test public void testfindUserByIds()throws Exception{ SqlSession session = factory.openSession(); UserMapper mapper = session.getMapper(UserMapper.class); QueryVo queryVo =new QueryVo(); List<Integer> integers = new ArrayList<>(); integers.add(1); integers.add(10); integers.add(31); queryVo.setIds(integers); List<User> users = mapper.findUserByIds(queryVo); System.out.println(users); }
对应的sql映射文件
<select id="findUserByIds" parameterType="com.wang.pojo.QueryVo" resultType="com.wang.pojo.User"> select * from user <where> <if test="ids !=null"> <!-- foreach:循环传入集合的参数 collection:传入的集合 item:每次循环将循环出的数据放入这个变量中 open:循环开始拼接的字符串 close:循环结束拼接的字符串 separator:循环中拼接分隔符 --> <foreach collection="ids" item="id" open="id in (" close=") " separator=","> #{id} </foreach> </if> </where> </select>
foreach:循环传入集合的参数 collection:传入的集合 item:每次循环将循环出的数据放入这个变量中 open:循环开始拼接的字符串 close:循环结束拼接的字符串 separator:循环中拼接分隔符
2.4sql片段
Sql中可将重复的sql提取出来,使用时用include引用即可,最终达到sql重用的目的,如下:
<!-- 封装sql条件,封装后可以重用 ID:是唯一表示 --> <sql id="userWhere"> <!-- where 标签作用: 1、会自动向sql中添加where关键字 2、会自动去掉第一个条件的and关键字 --> <where> <if test="username !=null and username !=''"> and username like '%${username}%' </if> <if test="sex !=null and sex !=''"> and sex =#{sex} </if> </where> </sql>
引入sql片段:
<select id="findUserByUserNameAndSex" parameterType="com.wang.pojo.User" resultType="com.wang.pojo.User"> select * from user <include refid="userWhere"></include> </select>
3、关联查询
两个表:
顾客表:
CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(32) NOT NULL COMMENT '用户名称', `birthday` date DEFAULT NULL COMMENT '生日', `sex` char(1) DEFAULT NULL COMMENT '性别', `address` varchar(256) DEFAULT NULL COMMENT '地址', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=32 DEFAULT CHARSET=utf8;
订单表:
CREATE TABLE `orders` ( `id` int(11) NOT NULL AUTO_INCREMENT, `user_id` int(11) NOT NULL COMMENT '下单用户id', `number` varchar(32) NOT NULL COMMENT '订单号', `createtime` datetime NOT NULL COMMENT '创建订单时间', `note` varchar(100) DEFAULT NULL COMMENT '备注', PRIMARY KEY (`id`), KEY `FK_orders_1` (`user_id`), CONSTRAINT `FK_orders_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
3.1一对一
对于两表:商品中订单与顾客的关系为一对一
对应的sql语句如下:
select a.*,b.id uid,username,birthday,sex,address from orders a,user b where a.user_id = b.id
<!-- resultType返回的属性名与列命一直 --> <select id="findOrdersAndUser1" resultType="com.wang.pojo.CustomOrders"> select a.*,b.id uid,username,birthday,sex,address from orders a,user b where a.user_id = b.id </select>
3.1.1.解法一:自动自动映射
新键一个pojo让其继承Orders,之后在平铺User中所有的属性(决不可直接把user当做属性)
如下:
public class CustomOrders extends Orders{ //不能直接声明为user private int uid; //resultType返回的属性域列命一致,故为uid private String username;// 用户姓名 private String sex;// 性别 private Date birthday;// 生日 private String address;// 地址 public int getUid() { return uid; } public void setUid(int uid) { this.uid = uid; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
接口:
public List<CustomOrders> findOrdersAndUser1();
测试:
@Test public void findOrdersAndUser1() throws Exception{ SqlSession session = factory.openSession(); UserMapper mapper = session.getMapper(UserMapper.class); List<CustomOrders> customOrders = mapper.findOrdersAndUser1(); System.err.println(customOrders); }
3.1.2手动映射
a、定义po类
在Orders类中加入User属性,user属性中用于存储关联查询的用户信息,因为订单关联查询用户是一对一关系,所以这里使用单个User对象存储关联查询的用户信息。
public class Orders { private Integer id; private Integer userId; private String number; private Date createtime; private String note; private User user; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public Integer getUserId() { return userId; } public void setUserId(Integer userId) { this.userId = userId; } public String getNumber() { return number; } public void setNumber(String number) { this.number = number == null ? null : number.trim(); } public Date getCreatetime() { return createtime; } public void setCreatetime(Date createtime) { this.createtime = createtime; } public String getNote() { return note; } public void setNote(String note) { this.note = note == null ? null : note.trim(); } public User getUser() { return user; } public void setUser(User user) { this.user = user; } }
b、mapper.xml配置
<!--id:resultMap唯一标识 type:将查询出的数据放入指定的对象中 注意:手动映射需要:指定数据库中表的字段名与java中pojo类的属性名称对应关系 --> <!-- 以Orders为主,查询数据放入其中,把User放入其中当做属性 --> <resultMap type="com.wang.pojo.Orders" id="orderAndUserResultMap"> <!-- id标签指定主键字段对应关系 column:列,数据库中字段名称 property:pojo中属性名 --> <id column="id" property="id"/> <!--result指定非属性列的对应关系 --> <result column="user_id" property="userId"/> <result column="number" property="number"/> <result column="createtime" property="createtime"/> <result column="note" property="note"/> <!-- 指定单个对象的对应关系 property:指定将数据放入Oders中的user属性中 JavaType:支付user属性类型 --> <association property="user" javaType="com.wang.pojo.User"> <id column="uid" property="id"/> <result column="username" property="username"/> <result column="sex" property="sex"/> <result column="birthday" property="birthday"/> <result column="address" property="address"/> </association> </resultMap> <select id="findOrdersAndUser2" resultMap="orderAndUserResultMap"> select a.*,b.id uid,username,birthday,sex,address from orders a,user b where a.user_id = b.id </select>
新的方法:
resultMap标签:
id:resultMap唯一标识
type:将查询出的数据放入指定的对象中 注意:手动映射需要:指定数据库中表的字段名与java中pojo类的属性名称对应关系
以Orders为主,查询数据放入其中,把User放入其中当做属性
<id column="" property=""/> 指定id标签指定主键字段对应关系 column:列,数据库中字段名称 property:pojo中属性名
<result column="" property=""/> result指定非属性列的对应关系
<association property="user" javaType="com.wang.pojo.User">
指定单个对象的对应关系
property:指定将数据放入Oders中的user属性中
JavaType:支付user属性类型
<id column="uid" property="id"/>
<result column="username" property="username"/>
接口定义:
public List<Orders> findOrdersAndUser2();
测试:
@Test public void testFindOrdersAndUser2() throws Exception{ SqlSession session = factory.openSession(); UserMapper mapper = session.getMapper(UserMapper.class); List<Orders> customOrders = mapper.findOrdersAndUser2(); System.out.println(customOrders); }
3.2多对一:
顾客与订单是多对一:
select a.*,b.id oid,user_id,number,createtime from user a,orders b where a.id=b.user_id
以顾客为主,User新pojo(新的List<Orders> ordersList)
public class User { private int id; private String username;// 用户姓名 private String sex;// 性别 private Date birthday;// 生日 private String address;// 地址 private List<Orders> ordersList; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public List<Orders> getOrdersList() { return ordersList; } public void setOrdersList(List<Orders> ordersList) { this.ordersList = ordersList; } @Override public String toString() { return "User [id=" + id + ", username=" + username + ", sex=" + sex + ", birthday=" + birthday + ", address=" + address + "]"; } }
对应的sql映射:
<select id="findUserAndOrders" resultMap="userAndOrderResultMap"> select a.*,b.id oid,user_id,number,createtime from user a,orders b where a.id=b.user_id </select>
mapper.xml配置
<resultMap type="com.wang.pojo.User" id="userAndOrderResultMap"> <id column=" id" property="id"/> <result column="username" property="username"/> <result column="sex" property="sex"/> <result column="birthday" property="birthday"/> <result column="address" property="address"/> <!-- 指定对应的集合对象关系映射 property:将数据放入User对象中的ordersList中 ofType:指定ordersList的范型的类型 --> <collection property="ordersList" ofType="com.wang.pojo.Orders"> <id column="oid" property="id"/> <result column="user_id" property="userId"/> <result column="number" property="number"/> <result column="createtime" property="createtime"/> <result column="note" property="note"/> </collection> </resultMap> <select id="findUserAndOrders" resultMap="userAndOrderResultMap"> select a.*,b.id oid,user_id,number,createtime from user a,orders b where a.id=b.user_id </select>
标签resultMap中collection指定对应的集合对象关系映射
接口定义:
public List<User> findUserAndOrders();
测试:
@Test public void testfindUserAndOrders() throws Exception{ SqlSession session = factory.openSession(); UserMapper mapper = session.getMapper(UserMapper.class); List<User> customOrders = mapper.findUserAndOrders(); System.out.println(customOrders); }
整个:UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- mapper接口代理实现规则: 1、映射文件中namespace要等于接口的全路径名 2、映射文件中sql语句id要等于接口的方法名 3、映射文件中传入参数类型要等于接口方法的传入参数类型 4、映射文件中返回结果集类型要等于接口方法的返回值类型 --> <mapper namespace="com.wang.mapper.UserMapper"> <!-- 封装sql条件,封装后可以重用 ID:是唯一表示 --> <sql id="userWhere"> <!-- where 标签作用: 1、会自动向sql中添加where关键字 2、会自动去掉第一个条件的and关键字 --> <where> <if test="username !=null and username !=''"> and username like '%${username}%' </if> <if test="sex !=null and sex !=''"> and sex =#{sex} </if> </where> </sql> <!-- id:sql语句唯一表示 parameterType:指定传入参数类型,对应pojo属性的类型 resultType:返回结果类型 #{}占位符,起到站位的作用,如果传入的是基本类型(string、long、int、boolean、float等), 那么#{}中的变量名称可以是随意写,一般写属性 --> <select id="findUserById" parameterType="java.lang.Integer" resultType="com.wang.pojo.User"> SELECT * from user WHERE id =#{id} </select> <!-- 如果返回结果为集合,可以调用selectList方法,这个方法返回的结果就是一个集合,所映射的文件一个配置成集合范型的类型 ${}拼接符,字符串原样拼接,如果传入的是基本类型(string、long、int、boolean、float等), 那么%{}中的变量名称必须是value 注意:有sql注入的风险 --> <select id="findUserByUserName" parameterType="java.lang.String" resultType="com.wang.pojo.User"> SELECT * from user where username like '%${value}%' </select> <!-- #{}:如果传入的pojo类型,那么#{}中的变量名称必须是pojo中对应的属性.属性.属性..... 如果要返回数据库自增主键,使用SELECT LAST_INSERT_ID() --> <insert id="insertUser" parameterType="com.wang.pojo.User"> <!-- 执行SELECT LAST_INSERT_ID()数据库函数,返回自增主键 keyProperty:将返回的主键放入传入参数的Id中保存 order:当前函数相对于insert语句的执行顺序,在insert前是BEFORE之后是AFTER resultType:ID的类型,就是keyProperty中的类型 --> <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer"> SELECT LAST_INSERT_ID() </selectKey> INSERT INTO user (username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address}) </insert> <delete id="deleteUserById" parameterType="java.lang.Integer"> delete from user where id=#{id} </delete> <update id="updateById" parameterType="com.wang.pojo.User"> update user set username=#{username} where id=#{id} </update> <!-- 只有返回一行一列的时候,那么返回值类型才可以是指定成基本类型 --> <select id="findUserCount" resultType="java.lang.Integer"> select count(*) from user </select> <select id="findUserByUserNameAndSex" parameterType="com.wang.pojo.User" resultType="com.wang.pojo.User"> select * from user <include refid="userWhere"></include> </select> <select id="findUserByIds" parameterType="com.wang.pojo.QueryVo" resultType="com.wang.pojo.User"> select * from user <where> <if test="ids !=null"> <!-- foreach:循环传入集合的参数 collection:传入的集合 item:每次循环将循环出的数据放入这个变量中 open:循环开始拼接的字符串 close:循环结束拼接的字符串 separator:循环中拼接分隔符 --> <foreach collection="ids" item="id" open="id in (" close=") " separator=","> #{id} </foreach> </if> </where> </select> <!-- 一对一:自动映射 --> <!-- resultType返回的属性名与列命一直 --> <select id="findOrdersAndUser1" resultType="cn.itheima.pojo.CustomOrders"> select a.*,b.id uid,username,birthday,sex,address from orders a,user b where a.user_id = b.id </select> <!-- 一对一:手动映射 --> <!--id:resultMap唯一标识 type:将查询出的数据放入指定的对象中 注意:手动映射需要:指定数据库中表的字段名与java中pojo类的属性名称对应关系 --> <!-- 以Orders为主,查询数据放入其中,把User放入其中当做属性 --> <resultMap type="com.wang.pojo.Orders" id="orderAndUserResultMap"> <!-- id标签指定主键字段对应关系 column:列,数据库中字段名称 property:pojo中属性名 --> <id column="id" property="id"/> <!--result指定非属性列的对应关系 --> <result column="user_id" property="userId"/> <result column="number" property="number"/> <result column="createtime" property="createtime"/> <result column="note" property="note"/> <!-- 指定单个对象的对应关系 property:指定将数据放入Oders中的user属性中 JavaType:支付user属性类型 --> <association property="user" javaType="com.wang.pojo.User"> <id column="uid" property="id"/> <result column="username" property="username"/> <result column="sex" property="sex"/> <result column="birthday" property="birthday"/> <result column="address" property="address"/> </association> </resultMap> <select id="findOrdersAndUser2" resultMap="orderAndUserResultMap"> select a.*,b.id uid,username,birthday,sex,address from orders a,user b where a.user_id = b.id </select> <resultMap type="com.wang.pojo.User" id="userAndOrderResultMap"> <id column=" id" property="id"/> <result column="username" property="username"/> <result column="sex" property="sex"/> <result column="birthday" property="birthday"/> <result column="address" property="address"/> <!-- 指定对应的集合对象关系映射 property:将数据放入User对象中的ordersList中 ofType:指定ordersList的范型的类型 --> <collection property="ordersList" ofType="com.wang.pojo.Orders"> <id column="oid" property="id"/> <result column="user_id" property="userId"/> <result column="number" property="number"/> <result column="createtime" property="createtime"/> <result column="note" property="note"/> </collection> </resultMap> <select id="findUserAndOrders" resultMap="userAndOrderResultMap"> select a.*,b.id oid,user_id,number,createtime from user a,orders b where a.id=b.user_id </select> </mapper>
对应的UserMapper.java接口
package com.wang.mapper; import java.util.List; import cn.itheima.pojo.CustomOrders; import com.wang.pojo.Orders; import com.wang.pojo.QueryVo; import com.wang.pojo.User; public interface UserMapper { public com.wang.pojo.User findUserById(java.lang.Integer id); //动态代理形式中如果返回结果集为list,那么mybatis会生成实现类的使用 //会自动调用selectList方法 public List<User> findUserByUserName(String name); public void insertUser(User user); public Integer findUserCount(); public List<User> findUserByUserNameAndSex(User user); public List<User> findUserByIds(QueryVo vo); public List<CustomOrders> findOrdersAndUser1(); public List<Orders> findOrdersAndUser2(); public List<User> findUserAndOrders(); }
总结:
1、Mybatis解决jdbc编程的问题
1、 数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可解决此问题。
解决:在SqlMapConfig.xml中配置数据链接池,使用连接池管理数据库链接。
2、 Sql语句写在代码中造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变java代码。
解决:将Sql语句配置在XXXXmapper.xml文件中与java代码分离。
3、 向sql语句传参数麻烦,因为sql语句的where条件不一定,可能多也可能少,占位符需要和参数一一对应。
解决:Mybatis自动将java对象映射至sql语句,通过statement中的parameterType定义输入参数的类型。
4、 对结果集解析麻烦,sql变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成pojo对象解析比较方便。
解决:Mybatis自动将sql执行结果映射至java对象,通过statement中的resultType定义输出结果的类型。
2、mybatis与hibernate不同
Mybatis和hibernate不同,它不完全是一个ORM框架,因为MyBatis需要程序员自己编写Sql语句,不过mybatis可以通过XML或注解方式灵活配置要运行的sql语句,并将java对象和sql语句映射生成最终执行的sql,最后将sql执行的结果再映射生成java对象。
Mybatis学习门槛低,简单易学,程序员直接编写原生态sql,可严格控制sql执行性能,灵活度高,非常适合对关系数据模型要求不高的软件开发,例如互联网软件、企业运营类软件等,因为这类软件需求变化频繁,一但需求变化要求成果输出迅速。但是灵活的前提是mybatis无法做到数据库无关性,如果需要实现支持多种数据库的软件则需要自定义多套sql映射文件,工作量大。
Hibernate对象/关系映射能力强,数据库无关性好,对于关系模型要求高的软件(例如需求固定的定制化软件)如果用hibernate开发可以节省很多代码,提高效率。但是Hibernate的学习门槛高,要精通门槛更高,而且怎么设计O/R映射,在性能和对象模型之间如何权衡,以及怎样用好Hibernate需要具有很强的经验和能力才行。
总之,按照用户的需求在有限的资源环境下只要能做出维护性、扩展性良好的软件架构都是好架构,所以框架只有适合才是最好。