MyBatis入门的下篇,本来想一次性写完的.但是中间因为有点儿事情,就分开写了.基础的东西,就不再介绍了,具体可以看上篇,在我博客里搜索MyBatis就可以了
对于简单的CRUD,我们前面已经完成了,但是有一个特点那就是接口中要么没参数,要么只有一个参数,那么如果有多个参数,该怎么搞呢?其实在MyBatis中,多个参数是要做些小设置的,不是想当然的事儿.
多参数的解决有四种方法:
1.将参数封装到一个HashMap中进行传递
2.将参数封装到一个JavaBean中,并为参数提供getter/setter方法
3.使用@Param注解表示参数传递.
4.使用MyBatis的默认规则
以上的方法,用的比较多的,是第三种,使用注解来配置.其实使用注解,MyBatis底层也是把它封装成HashMap,key对应参数的名字,value对应传入的值.
下面看一个根据用户名和密码查询的例子,有两个参数,一个用户名一个密码.配置文件什么的就不全写了,沿用上篇的.
首先在接口中添加该方法
//根据用户名和密码查询用户public User queryByUsernameAndPasswd(@Param("userName")String username,@Param("password")String password);
@Param(“userName”) 这里就设置了传入参数的参数名,可以在Mapper文件中进行引用.
配置Mapper文件
<select id="queryByUsernameAndPasswd" resultType="User" > SELECT * FROM tb_user WHERE user_name=#{userName} and password=#{password} </select>
因为是查询,所以使用<select>标签,这样就可以使用到前面的注解设置的参数了.如果没有前面的注解,这里是会报错的.因为有两个参数,MyBatis不能对应,他不知道哪个参数对应哪个值.有了注解,就相当于有了HashMap,Map中的key,就是我们注解的内容,Map的value,就是传入的实际的值.所以,它根据Key和Value就可以一一对应上了.
如果不想使用注解,可以使用它的默认规则.其它MyBatis是有默认规则的,你不写注解.它的默认规则会认为你的传入的第一个参数是0,第二个是1,往后依次,或者第一个是param1,第二个是param2,往后依次.
所以,我们这里可以写成
<select id="queryByUsernameAndPasswd" resultType="User" > SELECT * FROM tb_user WHERE user_name=#{0} and password=#{1} </select>
或者
<select id="queryByUsernameAndPasswd" resultType="User" > SELECT * FROM tb_user WHERE user_name=#{param1} and password=#{param2} </select>
都是可以的.至于把参数封装成一个JavaBean和封装到HashMap就不演示了,都是一样的.
然后在测试类中测试.
@Testpublic void testQueryByUsernameAndPassword(){User user = dao.queryByUsernameAndPasswd("zhangsan", "123456");System.out.println(user);}
?
可以看到,结果出来了.
还有一个比较牛逼的就是MyBatis支持动态SQL和SQL片段
先说SQL片段和resultMap吧,这个简单.就是可以在Mapper配置文件中定义SQL片段.SQL片段就是一些公用的东西,可以被别的标签引用到.
看下面的例子.
<!-- 定义一个resultMap,这个主要是用于数据库的结果集和POJO类的映射和高级查询 --> <!-- 可以使用autoMapping来控制是否自动映射,自动映射就是下面列出来的,就照列出来的规则映射 没列出来的,使用默认规则映射.如果不开启,所以没有写映射规则的字段,将都是NULL,不会自动使用默认规则映射 --> <resultMap type="User" id="userResultMap" autoMapping="true" > <id column="id" property="id" /> <result column="user_name" property="userName" /> </resultMap> <!-- 使用resultMap --> <!-- 使用SQL片断 --> <select id="queryAll" resultMap="userResultMap"> select <include refid="allFields"/> from tb_user </select> <!-- 定义一个SQL片断,可以在别处引用,这里把tb_user表中的所有字段都列出来了,如果以后要用,可直接引用 --> <sql id="allFields" >id,user_name,password,name,age,sex,birthday,created,updated</sql>
上面的内容是Mapper配置文件中的.首先定义了一个resultMap,上面已经讲的很清楚了.不再细说.SQL片段比较神奇,可以把一些公用的抽出来,被其它的引用.定义SQL片段使用<sql>标签,然后起一个名字,具体内容放标签体中就行了.上面是把tb_user中的所有字段,都抽出来了.然后再查询所有的时候,直接使用这个片段,使用片段需要使用<include>标签来引入片段.
使用了resultMap,要把之前的resultType给去掉,换成resultMap
动态SQL就是支持SQL语句的动态拼接了,在拼接的时候,可以使用判断,循环结构,这个就比较强大了.
支持的动态SQL有
1.if
2.choose,when,otherwise
3.where,set
4.foreach
感觉这个玩意儿,跟Struts2的OGNL比较像.
通过这个,来完成几个功能.
接口中代码如下
//根据用户名和密码查询用户public User queryByUsernameAndPasswd(@Param("userName")String username,@Param("password")String password);//根据用户名模糊匹配,如果没有传入用户名,查询所有sex为1的用户public List<User> queryUserByLike(@Param("name")String name);//根据年龄和姓名查找,如果输入了年龄就按照年龄查找,如果输入了姓名,就按照姓名查找public List<User> queryUserByNameAndAge(@Param("name")String name,@Param("age") Integer age);//根据ids查找用户public List<User> queryUserByIds(@Param("ids")List<Integer> ids);
修改Mapper配置文件
<select id="queryUserByLike" resultType="User" > SELECT * FROM tb_user where sex=1 <if test="name != null and name !=''"> AND name LIKE #{name} </if> </select> <select id="queryUserByNameAndAge" resultType="User"> SELECT * FROM tb_user <where> <if test="name !=null and name !='' and age!=null and age!= ''"> AND name LIKE #{name} AND age=#{age} </if> <choose> <when test="name != null and name != ''"> AND name LIKE #{name} </when> <otherwise> AND age=#{age} </otherwise> </choose> </where> </select> <select id="queryUserByIds" parameterType="Integer" resultMap="userResultMap"> SELECT * FROM tb_user where id in <!-- collections就是传入的参数,item方便下面引用,open表示以什么开始,close表示以什么结束,最后一个指定分隔符 拼接的结果就是 (1,2,3) --> <foreach collection="ids" item="id" open="(" close=")" separator="," > #{id} </foreach> </select>
测试代码如下:
@Testpublic void testQueryByLike(){List<User> list = dao.queryUserByLike("%李%");for (User user : list) {System.out.println(user);}}@Testpublic void testqueryUserByNameAndAge(){List<User> list = this.dao.queryUserByNameAndAge("%李%", 21);for (User user : list) {System.out.println(user);}}@Testpublic void testQueryUserByIds(){List<Integer> ids = new ArrayList<Integer>();ids.add(1);ids.add(2);ids.add(3);List<User> list = dao.queryUserByIds(ids);for (User user : list) {System.out.println(user);}}
MyBatis的缓存跟hibernate的缓存理论都是一样的,分为一级缓存和二级缓存.
一级缓存是不能禁用的,而且是默认开启的,只在同一个session有效.当执行insert,update,delete语句的时候,一级缓存会失效.
二级缓存则需要配置,它有一个默认的提供缓存的类,也可以使用第三方的缓存,比如EhCache,Memcached等.二级缓存的是在整个sessionFactory有效,需要缓存的接口,必须失败序列化接口.因为它其实是把查询的结果缓存起来了,如果不实现序列化接口,则在序列化的时候,会报错.因为用的比较少,所以这里就不测试了.
MyBatis的高级查询
在开发中,我们经常会使用到高级查询,包括一对一,一对多,多对多三种.
四张表的对应关系如下图
?
?
?
?
?
?
?
?
?
?
?
需求如下接口中定义的方法
package org.linuxsogood.mybatis.advanceQuery;import org.apache.ibatis.annotations.Param;import org.linuxsogood.mybatis.pojo.Order;public interface AdvanceQuery {//一对一查询:查询订单,并且查询出下单人的信息。public Order queryOrderAndUser(@Param("orderId")String order_id);//一对多查询:查询订单,查询出下单人信息并且查询出订单详情。public Order queryOrderAndUserAndOrderdetail(@Param("orderId")String order_id);//多对多查询:查询订单,查询出下单人信息并且查询出订单详情中的商品数据。public Order queryOrderAndUserAndOrdertetailAndItem(@Param("orderId")String order_id);}
因为MyBatis要手动写SQL语句,不像hibernate那样可以直接写HQL.而且写完SQL语句,如果字段不对应,还要手动去映射或者开自动映射,自动映射也是有一定规则的.如果不符合规则,则必须手动去一个一个映射.
对于一对一的查询结果,我们返回的是Order,而查询还需要用户的信息,Order的POJO类中,并没有用户的信息,所以封装起来还是有点儿麻烦的.这里我们借鉴hibernate中的思想,在Order中定义一个User属性,并提供getter/setter方法.然后再去手动封装即可.MyBatis对这种情况,也提供了支持.
一对一查询,Mapper文件的写法.
<!-- 一对一查询 --> <select id="queryOrderAndUser" resultMap="orderAndUser" parameterType="String" > SELECT * from tb_order o LEFT JOIN tb_user u ON o.user_id=u.id WHERE order_number = #{orderId} </select> <!-- 定义一个用于查询一对一的resultMap --><resultMap type="Order" id="orderAndUser" autoMapping="true" ><id column="id" property="id" /><!-- 手动映射主键 --><result column="user_id" property="userId" /><!-- 联合映射,用来映射对象,property是Order中有一个属性叫user,javaType指定user的类型 并开启自动映射 --><association property="user" javaType="User" autoMapping="true"><!-- 手动映射user的主键 --><id column="user_id" property="id" /></association></resultMap>
因为这里使用了resultMap,所以在selete标签中,就不能使用resultResult了,resultMap和resultResult不能共存.
结果集中使用了<association>标签来对Order中的user对你做了一个映射.这个是专门用来映射对象属性的.
其中的property是Order中的那个对象属性的变量名,javaType是那个对象属性的类型,也就是是哪个POJO.autoMapping=”true” 开启自动映射,开启了自动映射我们只需要写一个主键的映射,其它的字段会被自动映射上去.这里写自动映射的标签,会报错.但是MyBatis底层已经实现了这个功能,报错可以不用理.DTD文件没有更新的缘故
?
一对多Mapper文件的写法.
<!-- 一对多查询 --> <select id="queryOrderAndUserAndOrderdetail" resultMap="orderAndUserAndDetail" parameterType="String"> SELECT * FROM tb_order o LEFT JOIN tb_user u ON o.user_id=u.id LEFT JOIN tb_orderdetail d ON o.id=d.order_id WHERE order_number=#{orderId} </select> <!-- 定义一对多查询的resultMap --> <resultMap type="Order" id="orderAndUserAndDetail" autoMapping="true" > <association property="user" javaType="User" autoMapping="true" > <id column="user_id" property="id" /> </association> <!-- 聚合结果集 --> <!-- ofType是List集合中放的数据类型 --> <collection property="orderdetails" javaType="List" ofType="Orderdetail" autoMapping="true" > <id column="id" property="id"/> </collection> </resultMap>
一对多中有一个不太好处理的就是我们返回的,还是Order对象,而我们查询的结果,确是多条的.怎么把这多条结果,放到一个Order中这是一个难题.因为Order和Ordertail的对应关系是一对多,所以我们在Order中定义一个List<Orderdetail>,这个也是hibernate中的思想,MyBatis也对其提供了支持.然后在Mapper中配置的时候,使用<collection>标签聚合结果集即可.
其实多个resultMap中可以使用extends来继承的.我这里没有使用.
<collection>中属性的说明:
property: 这个是指Order中的那个对象属性的名字.
javaType: 这个是指在Order中使用什么容器存放的Orderdetail对象
ofType: 这个是指容器中存放的对象类型.
autoMapping 开启自动映射,同样写上会报错.但是是可以使用的.
我在Order中定义的时候写的是: List<Orderdetail> orderdetails; 跟上面一一对应理解一下.
多对多Mapper的写法.
<!-- 多对多查询 --> <select id="queryOrderAndUserAndOrdertetailAndItem" resultMap="orderAndUserAndDetailAndItem" parameterType="String" > SELECT * FROM tb_order o LEFT JOIN tb_user u ON o.user_id=u.id LEFT JOIN tb_orderdetail d ON o.id=d.order_id LEFT JOIN tb_item i ON d.item_id=i.id WHERE order_number=#{orderId} </select> <!-- 定义一个多对多查询的resultMap --> <resultMap type="Order" id="orderAndUserAndDetailAndItem" autoMapping="true"> <association property="user" javaType="User" autoMapping="true" > <id property="id" column="id" /> </association> <collection property="orderdetails" ofType="Orderdetail" javaType="List" autoMapping="true" > <id column="id" property="id" /> <association property="item" javaType="Item" autoMapping="true" > <id property="id" column="id" /> </association> </collection> </resultMap>
只是前两种的一个结合体,没什么好讲的.
补充一下POJO和测试类
测试类
package org.linuxsogood.mybatis.advanceQuery;import static org.junit.Assert.*;import org.apache.ibatis.io.Resources;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import org.junit.Before;import org.junit.Test;import org.linuxsogood.mybatis.pojo.Order;public class AdvanceQueryTest {private AdvanceQuery mapper = null;@Beforepublic void setUp() throws Exception {SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));this.mapper = factory.openSession().getMapper(AdvanceQuery.class);}@Testpublic void testQueryOrderAndUser() {Order order = this.mapper.queryOrderAndUser("20140921001");System.out.println(order);}@Testpublic void testQueryOrderAndUserAndOrderdetail() {Order orderdetail = this.mapper.queryOrderAndUserAndOrderdetail("20140921001");System.out.println(orderdetail);}@Testpublic void testQueryOrderAndUserAndOrdertetailAndItem() {Order order = this.mapper.queryOrderAndUserAndOrdertetailAndItem("20140921001");System.out.println(order);}}
Order类
package org.linuxsogood.mybatis.pojo;import java.util.List;/** * 订单表 * */public class Order { private Integer id; private Long userId; private String orderNumber; private User user; private List<Orderdetail> orderdetails; public List<Orderdetail> getOrderdetails() {return orderdetails;}public void setOrderdetails(List<Orderdetail> orderdetails) {this.orderdetails = orderdetails;}public User getUser() { return user; } public void setUser(User user) { this.user = user; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public Long getUserId() { return userId; } public void setUserId(Long userId) { this.userId = userId; } public String getOrderNumber() { return orderNumber; } public void setOrderNumber(String orderNumber) { this.orderNumber = orderNumber; }@Overridepublic String toString() {return "Order [id=" + id + ", userId=" + userId + ", orderNumber="+ orderNumber + ", user=" + user + ", orderdetails="+ orderdetails + "]";}}
Item类
package org.linuxsogood.mybatis.pojo;/** * 商品表 */public class Item { private Integer id; private String itemName; private Float itemPrice; private String itemDetail; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getItemName() { return itemName; } public void setItemName(String itemName) { this.itemName = itemName; } public Float getItemPrice() { return itemPrice; } public void setItemPrice(Float itemPrice) { this.itemPrice = itemPrice; } public String getItemDetail() { return itemDetail; } public void setItemDetail(String itemDetail) { this.itemDetail = itemDetail; } @Override public String toString() { return "Item [id=" + id + ", itemName=" + itemName + ", itemPrice=" + itemPrice + ", itemDetail=" + itemDetail + "]"; } }
Orderdetail类
package org.linuxsogood.mybatis.pojo;public class Orderdetail { private Integer id; private Double totalPrice; private Integer status; private Integer orderId; private Integer itemId; private Item item; public Item getItem() {return item;}public void setItem(Item item) {this.item = item;}public Integer getOrderId() {return orderId;}public void setOrderId(Integer orderId) {this.orderId = orderId;}public Integer getItemId() {return itemId;}public void setItemId(Integer itemId) {this.itemId = itemId;}public Double getTotalPrice() { return totalPrice; } public void setTotalPrice(Double totalPrice) { this.totalPrice = totalPrice; } public Integer getStatus() { return status; } public void setStatus(Integer status) { this.status = status; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; }@Overridepublic String toString() {return "Orderdetail [id=" + id + ", totalPrice=" + totalPrice+ ", status=" + status + ", orderId=" + orderId + ", itemId="+ itemId + ", item=" + item + "]";}}
User类
package org.linuxsogood.mybatis.pojo;import java.util.Date;public class User implements java.io.Serializable{ private static final long serialVersionUID = 1L; private Long id; // 用户名 private String userName; // 密码 private String password; // 姓名 private String name; // 年龄 private Integer age; // 性别,1男性,2女性 private Integer sex; // 出生日期 private Date birthday; // 创建时间 private Date created; // 更新时间 private Date updated; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getuserName() { return userName; } public void setuserName(String userName) { this.userName = userName; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Integer getSex() { return sex; } public void setSex(Integer sex) { this.sex = sex; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public Date getCreated() { return created; } public void setCreated(Date created) { this.created = created; } public Date getUpdated() { return updated; } public void setUpdated(Date updated) { this.updated = updated; } @Override public String toString() { return "User [id=" + id + ", userName=" + userName + ", password=" + password + ", name=" + name + ", age=" + age + ", sex=" + sex + ", birthday=" + birthday + ", created=" + created + ", updated=" + updated + "]"; }}
?
画龙画虎难画骨,知人知面不知心。