分页、配置文件标签使用、动态SQL、关联查询
一、常用标签介绍
1.配置属性
properties(属性)
property
settings(全局配置参数)
setting
typeAliases(类型别名)
typeAliase
package
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境集合属性对象)
environment(环境子属性对象)
transactionManager(事务管理)
dataSource(数据源)
mappers(映射器)
mapper
package
① Properties属性
在使用 properties 标签配置时,我们可以采用两种方式指定属性配置。
第一种:
properties>
property name="jdbc.driver" value="com.mysql.jdbc.Driver"/>
property name="jdbc.url" value="jdbc:mysql://localhost:3306/test"/>
property name="jdbc.username" value="root"/>
property name="jdbc.password" value="root"/>
properties>
第二种:
在classpath下创建db.properties文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test
jdbc.username=root
jdbc.password=root
properties url= file:///D:/workspace/day02_test/src/db.properties">properties>
此时我们的 dataSource 标签就变成了引用上面的配置
dataSource type="POOLED">
property name="driver" value="${jdbc.driver}"/>
property name="url" value="${jdbc.url}"/>
property name="username" value="${jdbc.username}"/>
property name="password" value="${jdbc.password}"/>
dataSource>
② typeAliases属性
在前面我们讲的 Mybatis 支持的默认别名,我们也可以采用自定义别名方式来开发
在 mybatis-config.xml 中配置:
typeAliases>
typeAlias alias="user" type="com.tledu.zrz.pojo.User"/>
package name="com.tledu.zrz.pojo"/>
package name="其它包"/>
typeAliases>
③ Mappers属性
Mappers使我们所说的映射器,用于通过mybatis配置文件去找到对应的mapper文件
有三种用法
④Resources
使用相对于类路径的资源
如:
⑤class
使用 mapper 接口类路径
如:
注意:此种方法要求 mapper 接口名称和 mapper 映射文件名称相同,且放在同一个目录中。
⑥Package
注册指定包下的所有 mapper 接口
如:
注意:此种方法要求 mapper 接口名称和 mapper 映射文件名称相同,且放在同一个目录中。
-------------------------------------------------------------------------------------------------------------------------------------
2.Mapper.xml属性
1.#和$区别
#{}表示一个占位符号
通过#{}可以实现 preparedStatement 向占位符中设置值,自动进行 java 类型和 jdbc 类型转换,
#{}可以有效防止 sql 注入。
举例:select * from user where username = ' ' or 1=1 # and passord=''
#{}可以接收简单类型值或 pojo 属性值。 如果 parameterType 传输单个简单类
型值,#{}括号中可以是 value 或其它名称。
可以自动对值添加 ’ ’ 单引号
${}表示拼接 sql 串
通过${}可以将 parameterType 传入的内容拼接在 sql 中且不进行 jdbc 类型转换, ${}可以接收简
单类型值或 pojo 属性值,如果 parameterType 传输单个简单类型值,${}括号中只能是 value。
比如order by id 这种的,以id排序 那么这个id 是没有单引号的,就是简单的SQL拼接,所以我们应该使用${} 而不是#{}
举例: ALTER TABLE (表名) ADD keycode(字段) varchar(500)
-------------------------------------------------------------------------------------------------------------------------------------
2.parameterType 配置参数
我们在上一章节中已经介绍了 SQL 语句传参,使用标签的 parameterType 属性来设定。该属性的取值可以是基本类型,引用类型(例如:String 类型),还可以是实体类类型(POJO 类)。同时也可以使用实体类的包装类,本章节将介绍如何使用实体类的包装类作为参数传递。
注意事项
基本类型和String我们可以直接写类型名称,也可以使用包名.类名的方式
例如 :
java.lang.String
实体类类型,目前我们只能使用全限定类名。
究其原因,是 mybaits 在加载时已经把常用的数据类型注册了别名,从而我们在使用时可以不写包名,而我们的是实体类并没有注册别名,所以必须写全限定类名。在 mybatis 的官方文档的说明(第 19 页)
这些都是支持的默认别名。我们也可以从源码角度来看它们分别都是如何定义出来的。
可以参考 TypeAliasRegistery.class 的源码。
-------------------------------------------------------------------------------------------------------------------------------------
3.resultType
resultType 属性可以指定结果集的类型,它支持基本类型和实体类类型。
我们在前面的 CRUD 案例中已经对此属性进行过应用了。
需要注意的是,它和 parameterType 一样,如果注册过类型别名的,可以直接使用别名。没有注册过的必须使用全限定类名。
例如:我们的实体类此时必须是全限定类名
同时,当是实体类名称时,还有一个要求,实体类中的属性名称必须和查询语句中的列名保持一致,否则无法实现封装(不区分大小写)
-------------------------------------------------------------------------------------------------------------------------------------
4.resultMap
我们在resultType ,提到在声明返回值类型为实体类型之后,实体中的属性必须和查询语句中的属性对应上,但是我们在开发的过程中也难免会遇到无法对应的情况。比如说我们在进行数据库设计的时候,多个单词往往是用_连接,但是在实体类中的属性往往采用小驼峰的方式命名。这就导致字段名无法对应上,这个时候我们就需要配置resultMap来解决这个问题了。
通过resultMap,我们可以指定查询结果字段和实体属性字段的映射关系。
<resultMap id="userResult" type="User"> <id column="id" property="id" /> <result property="nickname" column="nickname" /> <result property="schoolName" column="school_name" /> </resultMap>
-------------------------------------------------------------------------------------------------------------------------------------
二、单元测试介绍
1、应用项目中导包
2、测试代码示例
//在所有@Test所标注的方法前都会先运行 @After public void after(){ System.out.println("after"); } //具体测试方法 @Test public void test1(){ System.out.println("test"); } //在所有@Test所标注的方法运行完后会运行 @Before public void before(){ System.out.println("before"); }
三、实现分页查询
分页是展示页面中常见的一种技术,就是把数据在页面分开显示,这样可以提高执行效率和用户的流量
比如我们有100条数据,用户在打开这个页面的时候需要加载100条数据,而后台需要处理100条数据
但是如果我们规定每页显示10条,也就是需要10页,这样每次只需要处理10条数据,客户每次只需要加载10条数据
那好像10页加起来也不会提高效率...
但是如果第二页就出现了你想要的数据,你还会查看第三页吗?
1、分页思想
看图分析:
如上图所示,t_user表共7条数据,如果按每页显示3条来算,共分3页
总条数 totalCount:select count(*) from t_user;
每页条数 pageSize: 3
共有几页 totalPage: totalCount / pageSize + (totalCount % pageSize == 0 ? 0 : 1)
通过分页limit查询参数变化规律 可以 发现
select * from t_user limit 起始值,分页单位
起始值一直在变化 ,而分页单设定好后是不变的
起始值 = (当前页-1)*分页单位
这样我们就可以根据以上规律实现sql的分页查询了
2、在UserMapper.xml中添加分页相关sql
<!-- 获取分页数据 --> <select id="findPageList" resultType="User" parameterType="map"> select * from t_user limit #{pageOffset},#{pageSize} </select> <!-- 获取总条数 --> <select id="getListCount" resultType="int"> select count(*) from t_user; </select>
3、测试
@Test public void test1(){ SqlSession session = MybatisUtil.getSession(); Map<String,Object> map = new HashMap<>(); int cpage = 2; int pageSize = 3; int start = (cpage-1)*pageSize; map.put("start",start); map.put("pageSize",pageSize); List<User> list = session.selectList("User.getList", map); for (User user : list) { System.out.println(user); } session.close(); }
分页插件的使用:
PageHelper是一款犀利的Mybatis分页插件,使用了这个插件之后,分页开发起来更加简单容易。因为是第三方插件,所以需要额外的jar包,都在右上角提供了下载:
配置插件
在mybatis-config.xml中,添加plugins标签配置,表示开启PageHelper插件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <settings> <!-- 打开延迟加载的开关 --> <setting name="lazyLoadingEnabled" value="true" /> <!-- 将积极加载改为消息加载即按需加载 --> <setting name="aggressiveLazyLoading" value="false"/> </settings> <typeAliases> <package name="com.how2java.pojo"/> </typeAliases> <plugins> <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin> </plugins> <environments default="development"> ..... </environments> <mappers> ... </mappers> </configuration>
修改CategoryMapper.xml
limit注释掉,因为分页相关工作,会由PageHelper去做掉,不需要自己去写了
<?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 namespace="com.how2java.pojo"> <select id="listCategory" resultType="Category"> select * from t_category <!-- <if test="start!=null and count!=null"> --> <!-- limit #{start},#{count} --> <!-- </if> --> </select> </mapper>
分页查询测试
查询很有意思,只需要在执行查询所有的调用之前,执行一条语句即可:
PageHelper.offsetPage(0, 5);
public class TestMybatis { public static void main(String[] args) throws IOException, InterruptedException { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = newSqlSessionFactoryBuilder().build(inputStream); SqlSession session = sqlSessionFactory.openSession(); PageHelper.offsetPage(0, 5); List<Category> cs = session.selectList("listCategory"); for (Category c : cs) { System.out.println(c.getName()); } session.commit(); session.close(); } }
获取总数
public class TestMybatis { public static void main(String[] args) throws IOException, InterruptedException { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = newSqlSessionFactoryBuilder().build(inputStream); SqlSession session = sqlSessionFactory.openSession(); PageHelper.offsetPage(0, 5); List<Category> cs = session.selectList("listCategory"); for (Category c : cs) { System.out.println(c.getName()); } PageInfo pageInfo = new PageInfo<>(cs); System.out.println("总数:"+pageInfo.getTotal()); System.out.println(pageInfo); session.commit(); session.close(); } }
-------------------------------------------------------------------------------------------------------------------------------------
四、完成模糊查询
模糊查询
1. 修改UserMapper.xml,提供listUserByName查询语句
<select id="listUserByName" parameterType="string" resultType="User"> select * from t_user where username like concat('%',#{0},'%') </select>
2. 测试
@Test public void listUserByName() { SqlSession session = null; try { session = MybatisUtil.getSession(); List<User> list = session.selectList("User.listUserByName","d"); for (User user : list) { System.out.println(user); } } catch (Exception e) { e.printStackTrace(); } finally { MybatisUtil.closeSession(session); } }
多条件查询
结合前面的模糊查询,多一个id大于多少的条件
1. UserMapper.xml 准备sql语句
<select id="listUserByIdAndName" parameterType="map" resultType="User"> select * from t_user where id> #{id} and username like concat('%',#{username},'%') </select>
2. 测试代码
因为是多个参数,而session.selectList()方法又只接受一个参数对象,所以需要把多个参数放在Map里,然后把这个Map对象作为参数传递进去
Map<String,Object> params = new HashMap<>(); params.put("id", 8); params.put("username", "d"); List<User> ulist= session.selectList("User.listUserByIdAndName",params);
-------------------------------------------------------------------------------------------------------------------------------------
五、掌握动态SQL
动态SQL是MyBatis的一个强大特性之一,如果你有使用 JDBC 或其他 相似框架的经验,你就明白条件地串联 SQL 字符串在一起是多么的痛苦,确保不能忘了空 格或在列表的最后省略逗号。动态 SQL 可以彻底处理这种痛苦。
动态 SQL 元素和使用 JSTL 或其他相似的基于 XML 的文本处理器相似。在 MyBatis 之 前的版本中,有很多的元素需要来了解。MyBatis 3 大大提升了它们,现在用不到原先一半 的元素就能工作了。MyBatis 采用功能强大的基于 OGNL 的表达式来消除其他元素。
· if
· choose (when, otherwise)
· where等
用法可参照API MyBatis3.2.3帮助文档(中文版).chm
创建产品表
create table t_product( id int NOT NULL AUTO_INCREMENT, name varchar(30) DEFAULT NULL, price float DEFAULT 0, cid int , PRIMARY KEY (id) )AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
新增6条产品数据
INSERT INTO t_product VALUES (1,'product a', 88.88, 1); INSERT INTO t_product VALUES (2,'product b', 88.88, 1); INSERT INTO t_product VALUES (3,'product c', 88.88, 1); INSERT INTO t_product VALUES (4,'product x', 88.88, 2); INSERT INTO t_product VALUES (5,'product y', 88.88, 2); INSERT INTO t_product VALUES (6,'product z', 88.88, 2);
创建Product实体类
public class Product { private int id; private String name; private float price; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public float getPrice() { return price; } public void setPrice(float price) { this.price = price; } @Override public String toString() { return "Product [id=" + id + ", name=" + name + ", price=" + price + "]"; }
动态SQL导入
执行不同的条件限定,需要准备两条sql语句
假设需要对Product执行两条sql语句,一个是查询所有,一个是根据名称模糊查询。
那么按照现在的方式,必须提供两条sql语句:listProduct和listProductByName
然后在调用的时候,分别调用它们来执行。
在ProductMapper.xml添加如下代码
<select id="listProduct" resultType="Product"> select * from t_product </select> <select id="listProductByName" resultType="Product"> select * from t_product where name like concat('%',#{name},'%') </select>
在测试类进行测试
System.out.println("查询所有的"); List<Product> ps = session.selectList("listProduct"); for (Product p : ps) { System.out.println(p); } System.out.println("模糊查询"); Map<String,Object> params = new HashMap<>(); params.put("name","a"); List<Product> ps2 = session.selectList("listProductByName",params); for (Product p : ps2) { System.out.println(p); }
if标签
如果Product的字段比较多的话,为了应付各个字段的查询,那么就需要写多条sql语句,这样就变得难以维护。这个时候,就可以使用Mybatis 动态SQL里的if标签
<select id="listProduct" resultType="Product"> select * from t_product <if test="name!=null"> where name like concat('%',#{name},'%') </if> </select>
在测试类进行测试
System.out.println("查询所有的"); List<Product> ps = session.selectList("listProduct"); for (Product p : ps) { System.out.println(p); } System.out.println("模糊查询"); Map<String,Object> params = new HashMap<>(); params.put("name","a"); List<Product> ps2 = session.selectList("listProductByName",params); for (Product p : ps2) { System.out.println(p); }
where标签
基于上一个知识点if 标签进行
在ProductMapper.xml添加如下代码
如果要进行多条件判断,就会写成这样:
<select id="listProduct" resultType="Product"> select * from t_product <if test="name!=null"> where name like concat('%',#{name},'%') </if> <if test="price!=0"> and price > #{price} </if> </select>
这么写的问题是:当没有name参数,却有price参数的时候,执行的sql语句就会是:
select * from t_product and price > 10
这样执行就会报错
这个问题可以通过标签来解决,如代码所示
<select id="listProduct" resultType="Product"> select * from t_product <where> <if test="name!=null"> and name like concat('%',#{name},'%') </if> <if test="price!=null and price!=0"> and price > #{price} </if> </where> </select>
标签会进行自动判断
如果任何条件都不成立,那么就在sql语句里就不会出现where关键字
如果有任何条件成立,会自动去掉多出来的 and 或者 or。
所以在测试代码里
Map<String,Object> params = new HashMap<>(); //params.put("name","a"); params.put("price","10");
这个参数map,无论是否提供值都可以正常执行
set标签
与where标签类似的,在update语句里也会碰到多个字段相关的问题。 在这种情况下,就可以使用set标签:
<update id="updateProduct" parameterType="Product" > update t_product <set> <if test="name != null">name=#{name},</if> <if test="price != null">price=#{price}</if> </set> where id=#{id} </update>
其效果与where标签类似,有数据的时候才进行设置。
测试:
Product p = new Product(); p.setId(6); p.setName("product zz"); p.setPrice(99.99f); session.update("updateProduct",p);
trim标签
trim 用来定制想要的功能,比如where标签就可以用
prefix :内容之前加的前缀
suffix :内容之后加的后缀
prefixOverrides: 属性会忽略通过管道分隔的文本序列
...
来替换
set标签就可以用
...
来替换
测试ProductMapper.xml
<select id="listProduct" resultType="Product"> select * from t_product <trim prefix="WHERE" prefixOverrides="AND |OR "> <if test="name!=null"> name like concat('%',#{name},'%') </if> <if test="price!=null and price!=0"> price > #{price} </if> </trim> </select> <update id="updateProduct" parameterType="Product" > update t_product <trim prefix="SET" suffixOverrides=","> <if test="name != null">name=#{name},</if> <if test="price != null">price=#{price},</if> </trim> where id=#{id} </update>
choose、when、otherwise
类似于Java中的switch case default
在ProductMapper.xml文件中添加如下代码
<select id="listProduct" resultType="Product"> SELECT * FROM t_product <where> <choose> <when test="name != null"> and name like concat('%',#{name},'%') </when> <when test="price !=null and price != 0"> and price > #{price} </when> <otherwise> and id >1 </otherwise> </choose> </where> </select>
在测试类中添加如下代码测试
Map<String,Object> params = new HashMap<>(); // params.put("name","a"); // params.put("price","10"); List<Product> ps = session.selectList("listProduct",params); for (Product p : ps) { System.out.println(p); }
foreach标签
ProductMapper.xml
foreach的主要用在构建in条件中,它可以在SQL语句中进行迭代一个集合。
foreach元素的属性主要有 item,index,collection,open,separator,close。
item集合中每一个元素进行迭代时的别名,
open该语句以什么开始,
separator在每次进行迭代之间以什么符号作为分隔 符,
close以什么结束,
在使用foreach的时候最关键的也是最容易出错的就是collection属性,
该属性是必须指定的,但是在不同情况下,该属性的值是不一样的,
主要有一下3种情况:
1. 如果传入的是单参数且参数类型是一个List的时候,collection属性值为list
2. 如果传入的是单参数且参数类型是一个array数组的时候,collection的属性值为array
3. 如果传入的参数是多个的时候,我们就需要把它们封装成一个Map了
<select id="listProduct" resultType="Product"> SELECT * FROM t_product WHERE ID in <foreach item="item" index="index" collection="list" open="(" separator="," close=")"> #{item} </foreach> </select>
在测试类中添加如下代码测试
List<Integer> ids = new ArrayList(); ids.add(1); ids.add(3); ids.add(5); List<Product> ps = session.selectList("listProduct",ids); for (Product p : ps) { System.out.println(p); }
ids.add(1);
bind标签
ProductMapper.xml
<!-- 本来的模糊查询方式 --> <!-- <select id="listProduct" resultType="Product"> --> <!-- select * from t_product where name like concat('%',#{0},'%') --> <!-- </select> --> <select id="listProduct" resultType="Product"> <bind name="likename" value="'%' + name + '%'" /> select * from t_product where name like #{likename} </select>
在测试类中添加如下代码测试
Map<String, String> params =new HashMap(); params.put("name", "product"); List<Product> ps = session.selectList("listProduct",params); for (Product p : ps) { System.out.println(p); }
六、关联查询
在项目中,某些实体类之间肯定有关联关系,比如一对一、多对一、一对多、多对多等,在mybatis 中可以通过association和collection,来处理这些关联关系。
1、一对多
对于产品类型来说,一种产品类型就对应了多个产品,那么在mybatis中我们可以通过collections解决。
1.1、创建产品表
create table t_product( id int NOT NULL AUTO_INCREMENT, name varchar(30) DEFAULT NULL, price float DEFAULT 0, cid int , PRIMARY KEY (id) )AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
1.2、创建产品类型表
create table t_category ( id int(11) NOT NULL AUTO_INCREMENT, name varchar(32) DEFAULT NULL, PRIMARY KEY (id) ) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
1.3、清空t_category 和 t_product_ 表 新增2条分类数据,id分别是1,2 ;新增6条产品数据,分
别关联上述2条分类数据
delete from t_category; INSERT INTO t_category VALUES (1,'category1'); INSERT INTO t_category VALUES (2,'category2'); delete from t_product; INSERT INTO t_product VALUES (1,'product a', 88.88, 1); INSERT INTO t_product VALUES (2,'product b', 88.88, 1); INSERT INTO t_product VALUES (3,'product c', 88.88, 1); INSERT INTO t_product VALUES (4,'product x', 88.88, 2); INSERT INTO t_product VALUES (5,'product y', 88.88, 2); INSERT INTO t_product VALUES (6,'product z', 88.88, 2);
1.4、创建Product实体类
public class Product { private int id; private String name; private float price; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public float getPrice() { return price; } public void setPrice(float price) { this.price = price; } @Override public String toString() { return "Product [id=" + id + ", name=" + name + ", price=" + price + "]"; }
1.5、创建Category实体
public class Category { private int id; private String name; List<Product> products; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public List<Product> getProducts() { return products; } public void setProducts(List<Product> products) { this.products = products; } @Override public String toString() { return "Category [id=" + id + ", name=" + name + "]"; } }
1.6、创建CategoryMapper.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 namespace="com.test.pojo"> <resultMap type="Category" id="categoryBean"> <id column="cid" property="id" /> <result column="cname" property="name" /> <!-- 一对多的关系 --> <!-- property: 指的是集合属性的值, ofType:指的是集合中元素的类型 --> <collection property="products" ofType="Product"> <id column="pid" property="id" /> <result column="pname" property="name" /> <result column="price" property="price" /> </collection> </resultMap> <!-- 关联查询分类和产品表 --> <select id="listCategory" resultMap="categoryBean"> select c.*, p.*, c.id 'cid', p.id 'pid', c.name 'cname', p.name 'pname' from t_category c left join t_product p on c.id = p.cid </select> </mapper>
1.7、创建MyBatis测试类
public static void main(String[] args) throws IOException { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = newSqlSessionFactoryBuilder().build(inputStream); SqlSession session = sqlSessionFactory.openSession(); List<Category> cs = session.selectList("listCategory"); for (Category c : cs) { System.out.println(c); List<Product> ps = c.getProducts(); for (Product p : ps) { System.out.println("\t"+p); } } session.commit(); session.close(); }
2、多对一
对于产品类型来说,一种类型就对应了多个产品,但对于产品来说只能对应一种产品类型,这种情况,在mybatis中就可以通过association 解决。本知识点建立在一对多的基础上讲解多对一关系。
2.1、修改Product.java 添加Category对象属性
public class Product { private int id; ...... private Category category; public Category getCategory() { return category; } public void setCategory(Category category) { this.category = category; } ...... @Override public String toString() { return "Product [id=" + id + ", name=" + name + ", price=" + price + "]"; } }
2.2、创建ProductMapper.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 namespace="com.hanlin.pojo"> <resultMap type="Product" id="productBean"> <id column="pid" property="id" /> <result column="pname" property="name" /> <result column="price" property="price" /> <!-- 多对一的关系 --> <!-- property: 指的是属性名称, javaType:指的是属性的类型 --> <association property="category" javaType="Category"> <id column="cid" property="id"/> <result column="cname" property="name"/> </association> </resultMap> <!-- 根据id查询Product, 关联将Orders查询出来 --> <select id="listProduct" resultMap="productBean"> select c.*, p.*, c.id 'cid', p.id 'pid', c.name 'cname', p.name 'pname' from t_category c left join t_product p on c.id = p.cid </select> </mapper>
2.3、修改mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <typeAliases> <package name="com.mjw.pojo"/> </typeAliases> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/ssm?characterEncoding=UTF-8"/> <property name="username" value="root"/> <property name="password" value="admin"/> </dataSource> </environment> </environments> <mappers> <mapper resource="com/mjw/pojo/Category.xml"/> <mapper resource="com/mjw/pojo/Product.xml"/> </mappers> </configuration>
2.4、在测试类中测试
public static void main(String[] args) throws IOException { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = newSqlSessionFactoryBuilder().build(inputStream); SqlSession session = sqlSessionFactory.openSession(); List<Product> ps = session.selectList("listProduct"); for (Product p : ps) { System.out.println(p+" 对应的分类是 \t "+ p.getCategory()); } session.commit(); session.close(); }
3、多对多
本知识点是基于多对一的基础上进行。
在学习之前首先要理清楚多对多的关系,这里以订单Order和产品Product为例:
一张订单里 可以包含多种产品
一种产品 可以出现在多张订单里
这就是多对多关系
为了维系多对多关系,必须要一个中间表。 在这里我们使用订单项(OrderItem)表来作为中间表
3.1、创建表
create table t_order ( id int(11) NOT NULL AUTO_INCREMENT, code varchar(32) DEFAULT NULL, PRIMARY KEY (id) ) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; create table t_order_item( id int(11) NOT NULL AUTO_INCREMENT, oid int , pid int , number int , PRIMARY KEY(id) )AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
3.2、插入数据
在一对多关系中准备的数据里已经有Product数据里,这里就只准备订单数据和订单项数据:
1. 插入两个订单
2. 插入6条订单项数据,建立如下关系
2.1 订单1对应产品 1,2,3
2.2 订单2对应产品 2,3,4
INSERT INTO t_order VALUES (1,'code000A'); INSERT INTO t_order VALUES (2,'code000B'); INSERT INTO t_order_item VALUES (null, 1, 1, 100); INSERT INTO t_order_item VALUES (null, 1, 2, 100); INSERT INTO t_order_item VALUES (null, 1, 3, 100); INSERT INTO t_order_item VALUES (null, 2, 2, 100); INSERT INTO t_order_item VALUES (null, 2, 3, 100); INSERT INTO t_order_item VALUES (null, 2, 4, 100);
3.3、创建OrderMapper.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 namespace="com.how2java.pojo"> <resultMap type="Order" id="orderBean"> <id column="oid" property="id" /> <result column="code" property="code" /> <collection property="orderItems" ofType="OrderItem"> <id column="oiid" property="id" /> <result column="number" property="number" /> <association property="product" javaType="Product"> <id column="pid" property="id"/> <result column="pname" property="name"/> <result column="price" property="price"/> </association> </collection> </resultMap> <select id="listOrder" resultMap="orderBean"> select o.*,p.*,oi.*, o.id 'oid', p.id 'pid', oi.id 'oiid', p.name 'pname' from t_order o left join t_order_item oi on o.id =oi.oid left join t_product p on p.id = oi.pid </select> <select id="getOrder" resultMap="orderBean"> select o.*,p.*,oi.*, o.id 'oid', p.id 'pid', oi.id 'oiid', p.name 'pname' from t_order o left join t_order_item oi on o.id =oi.oid left join t_product p on p.id = oi.pid where o.id = #{id} </select> </mapper>
3.4、创建OrderItemMapper.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 namespace="com.how2java.pojo"> <insert id="addOrderItem" parameterType="OrderItem"> insert into t_order_item values(null,#{order.id},#{product.id},#{number}) </insert> <insert id="deleteOrderItem" parameterType="OrderItem"> delete from t_order_item where oid = #{order.id} and pid = #{product.id} </insert> </mapper>
3.5、修改ProductMapper.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 namespace="com.how2java.pojo"> <resultMap type="Product" id="productBean"> <id column="pid" property="id" /> <result column="pname" property="name" /> <result column="price" property="price" /> <!-- 多对一的关系 --> <!-- property: 指的是属性名称, javaType:指的是属性的类型 --> <association property="category" javaType="Category"> <id column="cid" property="id"/> <result column="cname" property="name"/> </association> </resultMap> <select id="listProduct" resultMap="productBean"> select c.*, p.*, c.id 'cid', p.id 'pid', c.name 'cname', p.name 'pname' from t_category c left join t_product p on c.id = p.cid </select> <select id="getProduct" resultMap="productBean"> select c.*, p.*, c.id 'cid', p.id 'pid', c.name 'cname', p.name 'pname' from t_category c left join t_product p on c.id = p.cid where p.id = #{id} </select> </mapper>
3.6、修改mybatis-config.xml
添加对于OrderMapper.xml和OrderItemMapper.xml的映射
<!-- 加载sql映射文件 --> <mappers> <mapper resource="com/test/pojo/ProductMapper.xml"/> <mapper resource="com/test/pojo/OrderMapper.xml"/> <mapper resource="com/test/pojo/OrderItemMapper.xml"/> </mappers>
3.7、查询操作
查询出所有的订单,然后遍历每个订单下的多条订单项,以及订单项对应的产品名称,价格,购买数量,通过Order.xml的listOrder对应的sql语句进行查询:
<select id="listOrder" resultMap="orderBean"> select o.*,p.*,oi.*, o.id 'oid', p.id 'pid', oi.id 'oiid', p.name 'pname' from t_order o left join t_order_item oi on o.id =oi.oid left join t_product p on p.id = oi.pid </select>
联合t_order, t_order_item, t_product_ 三张表进行查询
<resultMap type="Order" id="orderBean"> <id column="oid" property="id" /> <result column="code" property="code" /> <collection property="orderItems" ofType="OrderItem"> <id column="oiid" property="id" /> <result column="number" property="number" /> <association property="product" javaType="Product"> <id column="pid" property="id"/> <result column="pname" property="name"/> <result column="price" property="price"/> </association> </collection> </resultMap>
查询结果 id和code字段放在Order对象里, 然后通过一对多的标签把oiid和number放在OrderItem对象里,最后把pid,pname,price放进Product对象里。
3.8、测试类中查询订单信息测试
List<Order> olist = session.selectList("listOrder"); for (Order o : olist) { System.out.println(o.getId()+"\t"+o.getCode()); List<OrderItem> items = o.getOrderItems(); for (OrderItem i : items) { System.out.println("商品信息="+i.getProduct()+"商品数量"+i.getNumber()); } } List<Order> olist = session.selectList("getOrder",1); for (Order o : olist) { System.out.println(o.getId()+"\t"+o.getCode()); List<OrderItem> items = o.getOrderItems(); for (OrderItem i : items) { System.out.println("商品信息="+i.getProduct()+"商品数量"+i.getNumber()); } }
3.9、建立订单000A和产品z的关系
首先通过id分别获取Ordre对象和Product对象,然后创建一个新的OrderItem对象,接着设置Order,设置Product,设置数量,最后调用"addOrderItem" 对应的sql语句插入数据。
Order o1 = session.selectOne("getOrder", 1); Product p6 = session.selectOne("getProduct", 6); OrderItem oi = new OrderItem(); oi.setProduct(p6); oi.setOrder(o1); oi.setNumber(200); session.insert("addOrderItem", oi);
3.10、删除关系
删除了订单00A和产品z的关系,再次查询,就看不到产品z了。
删除关系的时候,通过订单id(1)和产品id(6)进行删除。
其实所谓的删除关系,就是删除掉OrderItem记录。
Order o1 = session.selectOne("getOrder",1); Product p6 = session.selectOne("getProduct",6); OrderItem oi = new OrderItem(); oi.setProduct(p6); oi.setOrder(o1); session.delete("deleteOrderItem", oi);
3.11、修改关系
多对多不存在修改关系的做法,就是删除旧的,然后新增一条即达到修改的效果。
练习-删除订单时,删除对应的订单项
比如删除订单A,那么就应该删除订单A在订单项里所对应的数据。
提示,通过Mybatis执行多条sql语句需要增加一个参数:allowMultiQueries
<property name="url" value="jdbc:mysql://localhost:3306/1808b?characterEncoding=UTF-8&allowMultiQueries=true"/>
在OrderMapper.xml中添加
<delete id="deleteOrder" > delete from order_item_ where oid = #{id}; delete from order_ where id= #{id}; </delete>
测试类测试
int id = 1; session.delete("deleteOrder",id);
-------------------------------------------------------------------------------------------------------------------------------------
补充整理相关知识
ParameterType常见传入参数
2.1. 传入简单类型
Java代码:
public User get(Long id) { return (User) getSqlSession().selectOne("com.mjw.get" , id); }
MAPPER :
<select id="findUserListByIdList" parameterType="java.lang.Long" resultType="User"> select * from user where id = #{id}; </select>
2.2. 传入List
JAVA代码:
public List<Area> findUserListByIdList(List<Long> idList) { return getSqlSession().selectList("com.mjw.findUserListByIdList", idList); }
MAPPER :
<select id="findUserListByIdList" parameterType="java.util.ArrayList" resultType="User"> select * from user user <where> user.ID in ( <foreach item="guard" index="index" collection="list" separator=","> #{guard} </foreach> ) </where> </select>
单独传入list时,foreach中的collection必须是list,不不管变量的具体名称是什么。比如这里变量名为idList,collection却是是list。
2.3. 传入数组
JAVA代码:
public List<Area> findUserListByIdList(int[] ids) { return getSqlSession().selectList("com.mjw.findUserListByIdList", ids); }
MAPPER :
<select id="findUserListByIdList" parameterType="java.util.HashList" resultType="User"> select * from user user <where> user.ID in ( <foreach item="guard" index="index" collection="array" separator=","> #{guard} </foreach> ) </where> </select>
单独传入数组时,foreach中的collection必须是array,不不管变量的具体名称是什么。比如这里变量名为ids,collection却是是array
2.4. 传入map
JAVA代码:
public boolean exists(Map<String, Object> map){ Object count = getSqlSession().selectOne("com.mjw.exists", map); int totalCount = Integer.parseInt(count.toString()); return totalCount > 0 ? true : false; }
MAPPER :
<select id="exists" parameterType="java.util.HashMap" resultType="java.lang.Integer"> SELECT COUNT(*) FROM USER user <where> <if test="code != null"> and user.CODE = #{code} </if> <if test="id != null"> and user.ID = #{id} </if> <if test="idList !=null "> and user.ID in ( <foreach item="guard" index="index" collection="idList" separator=","> #{guard} </foreach> ) </if> </where> </select>
MAP中有list或array时,foreach中的collection必须是具体list或array的变量名。比如这里MAP含有一个名为idList的list,所以MAP中用idList取值,这点和单独传list或array时不太一样。
2.5. 传入JAVA对象
JAVA代码:
public boolean findUserListByDTO(UserDTO userDTO){ Object count = getSqlSession().selectOne("com.mjw.exists", userDTO); int totalCount = Integer.parseInt(count.toString()); return totalCount > 0 ? true : false; }
MAPPER :
<select id="findUserListByDTO" parameterType="UserDTO" resultType="java.lang.Integer"> SELECT COUNT(*) FROM USER user <where> <if test="code != null"> and user.CODE = #{code} </if> <if test="id != null"> and user.ID = #{id} </if> <if test="idList !=null "> and user.ID in ( <foreach item="guard" index="index" collection="idList" separator=","> #{guard} </foreach> ) </if> </where> </select>
JAVA对象中有list或array时,foreach中的collection必须是具体list或array的变量名。比如这里UserDTO含有一个名为idList的list,所以UserDTO中用idList取值,这点和单独传list或array时不太一样。