6.2.3. 占位符与拼接符区别
1. 类型处理:
占位符#{}传递参数时会做参数类型处理,
拼接符${}传递参数时不会做类型处理只进行字符串原样拼接
2. 安全性:
${}的原样拼接导致它存在安全漏洞,容易产生SQL注入风险
#{}的类型处理会对参数中存在的SQL敏感字符先转义然后再映射给SQL,这就不会影响原先的SQL,因此可以有效防止SQL注入。
3. 工作中的应用:
由于拼接符${}存在安全隐患,因此在实际项目尽量使用占位符#{}
1. 课程计划
1、高级参数映射和返回值映射(重点)
a) Pojo包装pojo的参数映射
b) 当结果集列名与pojo属性名不一致的返回值映射
2、动态sql(重点)
3、关联查询结果(重点)
a) 一对一关联结果
b) 一对多关联结果
4、Mybatis整合spring
5、逆向工程
2. 事前代码准备
今天学习内容的练习主要以MyBatis动态代理的方式访问编写访问数据库的代码,因此参照昨天的工程重新创建一个新工程作为今天代码练习的集成,同时需要把一些动态代理需要的目录、空文件提前构建好,以方便后面使用。
2.1. 工程代码结构(UTF8)
2.2. Mapper映射文件及对应的接口文件
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="cn.itcast.dao.OrderMapper"> <!-- SQL -->
</mapper> |
OrderMapper.java
package cn.itcast.dao;
public interface OrderMapper {
} |
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 namespace="cn.itcast.dao.UserMapper"> <!-- SQL -->
</mapper> |
UserMapper.java
package cn.itcast.dao;
public interface UserMapper {
} |
2.3. POJO定义
1. 将昨天工程中的【User.java】拷贝到pojo的包下
2. 把【资料\03.pojo\Order.java】拷贝到pojo的包下。
2.4. 配置文件和属性文件
1. 把昨天工程中Source Folder【config】下的全部配置文件和属性文件拷贝过来。
2. 走查一下配置文件,把没有必要的注释删除,需要修改的配置修改。
<?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> <!-- 配置属性文件 --> <properties resource="jdbc.properties" />
<!-- 数据库环境的配置 --> <environments default="dev"> <!-- 开发数据库环境的配置 --> <environment id="dev"> <!-- 事务管理的配置 --> <transactionManager type="JDBC"/> <!-- 数据源配置:driver, url, username, password --> <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> </environment> </environments>
<!-- 配置映射文件 --> <mappers> <!-- 通过包扫描DAO接口的方式批量加载映射文件 --> <package name="cn.itcast.dao"/> </mappers> </configuration> |
2.5. 测试类
package mybatis2;
import java.io.InputStream;
import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Before; import org.junit.Test;
public class MyTest {
private SqlSessionFactory sqlSessionFactory;
// 测试初始化函数 @Before public void init() throws Exception { // 读取配置文件 InputStream inputStream = Resources.getResourceAsStream("MyBatisConfig.xml"); // 根据主配置文件创建会话工厂 sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); }
// 测试通过接口加载与之对应的映射文件 @Test public void test1() throws Exception { SqlSession sqlSession = null; try { sqlSession = sqlSessionFactory.openSession(); // 创建DAO的动态代理对象
// 执行数据库操作
} catch(Exception ex) { ex.printStackTrace(); throw ex; } finally { sqlSession.close(); } } } |
3. 高级输入映射(重点)
3.1. 综合查询
综合查询在实际业务需求中十分常见。综合查询页面往往是包含多种查询维度的条件,比如上面的截图就是淘宝的订单查询页面。我们看到查询订单的条件包括:订单基本信息、用户信息、售后信息。
如果持久层使用MyBatis,应该如何接收参数呢?
3.1.1. 需求
查询:用户名是姓王的并且手机是135开头的,订单状态是【待发货】的订单信息。
【SQL语句】有订单又有用户,SQL应该是一个关联查询:
SELECT o.orderId, o.userId, o.orderStatus, o.goodsId, o.createDateTime FROM order1 o, user u WHERE u.name LIKE '王%' AND u.mobile LIKE '135%' AND o.orderStatus = '02' AND o.userId = u.userId |
3.1.2. 定义传递综合查询条件的包装类型
因为查询条件是多方面的业务条件,所以我们需要定义一个专门用来传递条件参数的实体类,它可以包含各种各样的查询条件,比如可以包含简单类型、对象、数组、List等。
通常我们把保存查询条件的包装类称为QueryVo.java,其实就是普通的java bean。
本示例中,我们需要订单基本信息和用户信息作为查询条件,所以包括了用户信息和订单信息。
【QueryVo.java】
public class QueryVo { // 用户信息 private User user;
// 订单信息 private Order order;
setter/getter。。。。。 } |
思考一个问题:上面的Vo定义我们为什么不将用户的属性和订单的属性混合到一起放到这个Vo中呢?
混合到一起是没有错的,而且参数映射时也简单了,但是会让Vo变得很混乱,分不清属性谁是谁的,这就容易出现bug,因此我们应该采用面向对象的设计思想——封装,即封装成一个User对象和一个Order对象,这样就不混乱了,避免了出错的可能。
3.1.3. SQL映射文件
【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="cn.itcast.dao.OrderMapper"> <!-- SQL --> <!-- 根据QueryVo查询订单信息 --> <select id="findOrderByQueryVo" parameterType="cn.itcast.pojo.QueryVo" resultType="cn.itcast.pojo.Order"> SELECT o.orderId, o.userId, o.orderStatus, o.goodsId, o.createDateTime FROM order1 o, user u WHERE u.name LIKE #{user.name} AND u.mobile LIKE #{user.mobile} AND o.orderStatus = #{order.orderStatus} AND o.userId = u.userId </select> </mapper> |
3.1.4. 定义接口
【OrderMapper.java】
package cn.itcast.dao;
import cn.itcast.pojo.QueryVo; import cn.itcast.pojo.Order;
public interface OrderMapper {
// 根据综合查询条件查询订单信息 public Order findOrderByQueryVo(QueryVo vo) throws Exception; } |
3.1.5. 客户端测试程序
【MyTest.java】
// 测试根据QueryVo查询订单信息 @Test public void test1() throws Exception { SqlSession sqlSession = sqlSessionFactory.openSession(); // 创建DAO的动态代理对象 OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class); User user = new User(); Order order = new Order(); user.setName("王%"); user.setMobile("135%"); order.setOrderStatus("02"); QueryVo vo = new QueryVo(); vo.setOrder(order); vo.setUser(user); // 执行数据库操作 List<Order> orderList = orderMapper.findOrderByQueryVo(vo); System.out.println(orderList); sqlSession.close(); } |
<SQL映射规范>
·参数映射规范(四)
用包装类型传递参数时,parameterType="包装类型",占位符或拼接符的变量名等于包装类的属性.属性.属性...
4. 高级输出映射(重点)
Mybatis的返回值映射能将结果集映射成Java对象的关键是:列名==属性名
但如果列名≠属性名怎么办?确切点说如果列名不等于属性名时我们如何来做映射呢?
解决的办法:就是手动定义返回值映射。
4.1. 需求
根据订单id查询数据库中order2表的订单信息。但order2表的最大问题就是字段名是以下划线分割的,这与Order的pojo中的属性名不一致。
4.2. 手动定义返回值映射
4.2.1. 定义返回值映射
【OrderMapper.xml】
<说明>
项目 |
解释 |
<resultMap> |
用于自定义返回值映射的规则,即自定义哪个列名对应哪个属性名。 |
id |
返回值映射的唯一标识 |
type |
返回值映射中java对象的类型 |
<result> |
用于定义一个返回值映射规范的标签,一个<resultMap>可以包含多个<result> |
column |
返回值映射中的列名 |
property |
返回值映射中的属性名 |
<id> |
用于定义返回值映射中主键列名与字段名的映射关系。用法和<result>一模一样,只是增加可读性。 |
<SQL映射示例>
<!-- 自定义返回值映射的规范 --> <resultMap type="cn.itcast.pojo.Order" id="order2ResultMap"> <id column="order_id" property="orderId"/> <!-- <result column="order_id" property="orderId"/> --> <result column="user_id" property="userId"/> <result column="order_status" property="orderStatus"/> <result column="goods_id" property="goodsId"/> <result column="create_date_time" property="createDateTime"/> </resultMap> |
有了这个自定义了规范,即使列名≠属性名,MyBatis也可以利用这个自定义的规范进行返回值映射了。
4.2.2. SQL
【OrderMapper.xml】
<说明>
项目 |
解释 |
resultMap |
引用返回值映射的自定义规范 |
<SQL映射示例>
<!-- 根据id查询order2表的订单信息 --> <select id="findOrder2ById" parameterType="String" resultMap="order2ResultMap"> SELECT order_id, user_id, order_status, goods_id, create_date_time FROM order2 WHERE order_id = #{orderId} </select> |
4.2.3. 接口
【OrderMapper.java】
// 根据id查询订单信息 public Order findOrder2ById(String orderId) throws Exception; |
4.2.4. 客户端测试程序
【MyTest.java】
// 测试根据id查询订单信息 @Test public void test1() throws Exception { SqlSession sqlSession = sqlSessionFactory.openSession(); // 创建DAO的动态代理对象 OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class); // 执行数据库操作 Order orderInfo = orderMapper.findOrder2ById("6d081184-433e-11e7-ab09-448a5b6dba5c"); System.out.println(orderInfo); sqlSession.close(); } |
4.3. 自定义返回值映射的替代方案
利用SQL字段的别名满足返回值映射的列名==POJO属性名的要求。
4.3.1. SQL
【OrderMapper.xml】
<!-- 根据id查询order2表的订单信息2 --> <select id="findOrder2ById2" parameterType="String" resultType="cn.itcast.pojo.Order"> SELECT order_id as orderId, user_id as userId, order_status as orderStatus, goods_id as goodsId, create_date_time as createDateTime FROM order2 WHERE order_id = #{orderId} </select> |
4.3.2. 接口
【OrderMapper.java】
// 根据id查询订单信息2 public Order findOrder2ById2(String orderId) throws Exception; |
4.3.3. 客户端测试程序
【MyTest.java】
// 测试根据id查询订单信息 @Test public void test2() throws Exception { SqlSession sqlSession = sqlSessionFactory.openSession(); // 创建DAO的动态代理对象 OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class); // 执行数据库操作 // Order orderInfo = orderMapper.findOrderById("6d081184-433e-11e7-ab09-448a5b6dba5c"); Order orderInfo = orderMapper.findOrder2ById2("6d081184-433e-11e7-ab09-448a5b6dba5c"); System.out.println(orderInfo); sqlSession.close(); } |
5. 动态SQL(重点)
本章内容针对SQL映射,赋予SQL映射更加强大灵活的特性,让SQL映射更能适应复杂多变的参数请求。因此本节内容比较杂,内容也较多,但每块内容都相对独立,不难掌握。
5.1.1. 动态SQL条件
5.1.2. <if>标签
【OrderMapper.xml】
<说明>
项目 |
解释 |
<if> |
用于判断它包含的SQL语句是否需要添加。 |
test |
判断的逻辑条件,and表示与,or表示或, true时添加,false时忽略。 |
<SQL映射示例>
<!-- 根据动态条件查询订单信息 --> <select id="findOrderByQueryVo2" parameterType="cn.itcast.pojo.QueryVo" resultType="cn.itcast.pojo.Order"> SELECT o.orderId, o.userId, o.orderStatus, o.goodsId, o.createDateTime FROM order1 o, user u WHERE 1 = 1 <if test="user.name != null and user.name != ''"> AND u.name LIKE #{user.name} </if> <if test="user.mobile != null and user.mobile != ''"> AND u.mobile LIKE #{user.mobile} </if> <if test="order.orderStatus != null and order.orderStatus != ''"> AND o.orderStatus = #{order.orderStatus} </if> and o.userId = u.userId </select> |
【OrderMapper.java】
// 根据动态查询条件查询订单信息 public List<Order> findOrderByQueryVo2(CustomQueryVo vo) throws Exception; |
【MyTest.java】
// 测试根据动态查询条件查询订单信息 @Test public void test1() throws Exception { SqlSession sqlSession = sqlSessionFactory.openSession(); // 创建DAO的动态代理对象 OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class); User user = new User(); Order order = new Order(); user.setName("王%"); user.setMobile("135%"); order.setOrderStatus("02"); QueryVo vo = new QueryVo(); vo.setOrder(order); vo.setUser(user); // 执行数据库操作 // List<Order> orderList = orderMapper.findOrderByQueryVo(vo); List<Order> orderList = orderMapper.findOrderByQueryVo2(vo); System.out.println(orderList); sqlSession.close(); } |
if标签不仅仅用于动态条件,SQL中所有接收参数的部分都可以通过if判断决定是否要追加,比如udate更新中可以实现更新项目的动态更新:
<update id="updateUser" parameterType="cn.itcast.pojo.User"> UPDATE user SET <if test="name != null and name != ''"> name = #{name}, </if> <if test="mobile != null and mobile != ''"> mobile = #{mobile}, </if> <if test="sex != null and sex != ''"> sex = #{sex}, </if> <if test="age != null and age != ''"> age = #{age}, </if> <if test="address != null and address != ''"> address = #{address}, </if> WHERE userId = #{userId} </update> |
上面的示例是有缺陷的,这个等到后面的学习中会改进它,现在只是让大家看到if标签不仅仅应用于where条件。
5.2. 完善动态SQL条件
5.2.1. <where>标签
【OrderMapper.xml】
<说明>
项目 |
解释 |
<where> |
功能:用于形成完整的where条件 作用:它会自动添加where关键字 它还能去掉第一个条件前面的and或or 因此<where>可以和<if>标签组合实现更完美的动态条件。 |
<SQL映射示例>
<!-- 根据动态条件查询订单信息(改进) --> <select id="findOrderByQueryVo3" parameterType="cn.itcast.pojo.QueryVo" resultType="cn.itcast.pojo.Order"> SELECT o.orderId, o.userId, o.orderStatus, o.goodsId, o.createDateTime FROM order1 o, user u <where> <if test="user.name != null and user.name != ''"> AND u.name LIKE #{user.name} </if> <if test="user.mobile != null and user.mobile != ''"> AND u.mobile LIKE #{user.mobile} </if> <if test="order.orderStatus != null and order.orderStatus != ''"> AND o.orderStatus = #{order.orderStatus} </if> and o.userId = u.userId </where> </select> |
【OrderMapper.java】
// 根据动态查询条件查询订单信息(改进) public List<Order> findOrderByQueryVo3(CustomQueryVo vo) throws Exception; |
【MyTest.java】
// 测试根据动态查询条件查询订单信息 @Test public void test1() throws Exception { SqlSession sqlSession = sqlSessionFactory.openSession(); // 创建DAO的动态代理对象 OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class); User user = new User(); Order order = new Order(); user.setName("王%"); user.setMobile("135%"); order.setOrderStatus("02"); QueryVo vo = new QueryVo(); vo.setOrder(order); vo.setUser(user); // 执行数据库操作 // List<Order> orderList = orderMapper.findOrderByQueryVo(vo); // List<Order> orderList = orderMapper.findOrderByQueryVo2(vo); List<Order> orderList = orderMapper.findOrderByQueryVo3(vo); System.out.println(orderList); sqlSession.close(); } |
5.3. SQL代码片段的复用
5.3.1. 定义可重用的SQL代码段
【OrderMapper.xml】把【findOrderByQueryVo2】和【findOrderByQueryVo3】中的条件提取出去:
<说明>
项目 |
解释 |
<sql> |
定义可共用的SQL片段,将可共用的SQL片段(可以包含其他动态标签)包含在<sql></sql>中间 |
id |
共用SQL片段的唯一标识 |
<SQL映射示例>
<!-- 订单信息的查询条件 --> <sql id="order_query_condition"> <if test="user.name != null and user.name != ''"> AND u.name LIKE #{user.name} </if> <if test="user.mobile != null and user.mobile != ''"> AND u.mobile LIKE #{user.mobile} </if> <if test="order.orderStatus != null and order.orderStatus != ''"> AND o.orderStatus = #{order.orderStatus} </if> </sql> |
修改后的【findOrderByQueryVo2】和【findOrderByQueryVo3】
<说明>
项目 |
解释 |
<include> |
引用已经定义好的SQL片段 |
refid |
被引用的SQL片段id |
<SQL映射示例>
<!-- 根据动态条件查询订单信息 --> <select id="findOrderByQueryVo2" parameterType="cn.itcast.pojo.QueryVo" resultType="cn.itcast.pojo.Order"> SELECT o.orderId, o.userId, o.orderStatus, o.goodsId, o.createDateTime FROM order1 o, user u WHERE 1 = 1 <include refid="order_query_conditions"/> and o.userId = u.userId </select>
<!-- 根据动态条件查询订单信息(改进) --> <select id="findOrderByQueryVo3" parameterType="cn.itcast.pojo.QueryVo" resultType="cn.itcast.pojo.Order"> SELECT o.orderId, o.userId, o.orderStatus, o.goodsId, o.createDateTime FROM order1 o, user u <where> <include refid="order_query_conditions"/> and o.userId = u.userId </where> </select> |
可以测试一下,结果仍然可以执行。
5.4. 动态多值SQL条件:foreach
上面的屏幕尺寸是可以多选的。对于同一个条件可以选择多个条件值的情况下如何处理?
5.4.1. 处理in条件
比如根据多个订单状态查询订单信息,我们需要传递多个订单状态。我们可以有两种做法:
一种是在QueryVo中添加一个List<String>类型的属性,然后传递QueryVo给SQL
一种是可以直接传递List<String>对象给SQL。
1. 在QueryVo中定义List<String>类型的属性:
【QueryVo.java】
public class QueryVo { 。。。。。。。 // 订单状态列表 private List<String> orderStatusList; 。。。。。。。
/** * @return the orderStatusList */ public List<String> getOrderStatusList() { return orderStatusList; }
/** * @param orderStatusList the orderStatusList to set */ public void setOrderStatusList(List<String> orderStatusList) { this.orderStatusList = orderStatusList; } } |
【OrderMapper.xml】
<说明>
项目 |
解释 |
<foreach> |
在参数映射中,用于循环遍历集合类型的参数。 |
collection |
表示要循环遍历的集合对象名称 |
item |
每次遍历时使用的临时变量名称,在循环内部用占位符来引用 |
separator |
每次循环之间的分隔符号 |
open(可选) |
循环开始之前的SQL语句部分 |
close(可选) |
循环结束之后的SQL语句部分 |
<SQL映射示例>
<!-- 根据多个订单状态查询订单信息(Vo中包装List) --> <select id="findOrderByOrderStatus" parameterType="cn.itcast.pojo.QueryVo" resultType="cn.itcast.pojo.Order"> SELECT orderId,userId,orderStatus,goodsId,createDateTime FROM order1 WHERE <!-- 不带open和close属性的形式 --> orderStatus in ( <foreach collection="orderStatusList" item="orderStatus" separator=","> #{orderStatus} </foreach> ) 或者================================================== <foreach collection="orderStatusList" item="orderStatus" separator="," open="orderStatus in (" close=")"> #{orderStatus} </foreach> </select> |
<SQL映射规范>
·参数映射规范(五)——<foreach>循环标签专用
如果参数映射是包装类中含有List属性, 那<foreach>标签中的collection必须等于List属性名。
【OrderMapper.java】
// 根据多个订单状态查询订单信息(Vo包装List) public List<Order> findOrderByOrderStatus(CustomQueryVo vo) throws Exception; |
【MyTest.java】
// 根据多个订单状态查询订单信息(Vo包装List) @Test public void test1() throws Exception { SqlSession sqlSession = null; sqlSession = sqlSessionFactory.openSession(); // 创建DAO的动态代理对象 OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class); List<String> orderStatusList = new ArrayList<String>(); orderStatusList.add("01"); orderStatusList.add("02"); orderStatusList.add("03"); QueryVo vo = new QueryVo(); vo.setOrderStatusList(orderStatusList); // 执行数据库操作 List<Order> orderList = orderMapper.findOrderByOrderStatus(vo); System.out.println(orderList); sqlSession.close(); } |
2. 直接传递List:
【OrderMapper.xml】
<!-- 根据多个订单状态查询订单信息2(直接传递List) --> <select id="findOrderByOrderStatus2" parameterType="java.util.List" resultType="cn.itcast.pojo.Order"> SELECT orderId,userId,orderStatus,goodsId,createDateTime FROM order1 WHERE <foreach collection="list" item="orderStatus" separator="," open="orderStatus in (" close=")"> #{orderStatus} </foreach> </select> |
<SQL映射规范>
·参数映射规范(六)——<foreach>循环标签专用
如果参数映射直接是List类型,那<foreach>标签中的collection必须等于固定名称list(必须小写,不能变)
【OrderMapper.java】
// 根据多个订单状态查询订单信息2(直接传递List) public List<Order> findOrderByOrderStatus2(List<String> statusList) throws Exception; |
【MyTest.java】
// 根据多个订单状态查询订单信息1(Vo包装List) @Test public void test1() throws Exception { SqlSession sqlSession = null; sqlSession = sqlSessionFactory.openSession(); // 创建DAO的动态代理对象 OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class); List<String> orderStatusList = new ArrayList<String>(); orderStatusLis.add("01"); orderStatusLis.add("02"); orderStatusLis.add("03"); CustomQueryVo vo = new CustomQueryVo(); vo.setOrderStatusList(orderStatusLis); // 执行数据库操作 // List<Order> orderList = orderMapper.findOrderByOrderStatus1(vo); List<Order> orderList2 = orderMapper.findOrderByOrderStatus2(orderStatusLis); System.out.println(orderList2); sqlSession.close(); } |
5.4.2. 处理or条件
【OrderMapper.xml】
<!-- 根据多个订单状态查询订单信息3(or条件) --> <select id="findOrderByOrderStatus3" parameterType="java.util.List" resultType="cn.itcast.pojo.Order"> SELECT orderId,userId,orderStatus,goodsId,createDateTime FROM order1 WHERE <foreach collection="list" item="orderStatus" separator="or"> orderStatus = #{orderStatus} </foreach> </select> |
【OrderMapper.java】
// 根据多个订单状态查询订单信息3(or条件) public List<Order> findOrderByOrderStatus3(List<String> statusList) throws Exception; |
【MyTest.java】
// 根据多个订单状态查询订单信息 @Test public void test1() throws Exception { SqlSession sqlSession = null; sqlSession = sqlSessionFactory.openSession(); // 创建DAO的动态代理对象 OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class); List<String> orderStatusList = new ArrayList<String>(); orderStatusLis.add("01"); orderStatusLis.add("02"); orderStatusLis.add("03"); CustomQueryVo vo = new CustomQueryVo(); vo.setOrderStatusList(orderStatusLis); // 执行数据库操作 // List<Order> orderList = orderMapper.findOrderByOrderStatus1(vo); // List<Order> orderList2 = orderMapper.findOrderByOrderStatus2(orderStatusLis); List<Order> orderList3 = orderMapper.findOrderByOrderStatus3(orderStatusLis); System.out.println(orderList3); sqlSession.close(); } |
5.5. 小结
上面的动态标签: <if>, <where>, <sql>, <foreach>无论你如何使用我们始终要记住一个宗旨:
保证动态形成的SQL语句的语法正确
只要你能包装SQL语法正确, 你就可以灵活的使用这些动态标签实现你自己的动态SQL。
6. 关联查询结果(重点)
6.1. 商品订单数据模型
注意:这里面两个表的关联都是由SQL控制的,跟MyBatis一点关系都没有。
MyBatis要解决的是怎样把具有关联关系的结果集通过返回值映射返回给Java程序。
6.2. 一对一查询结果集
6.2.1. SQL语句
SELECT o.orderId, o.goodsId, o.orderStatus, u.name, u.address, u.mobile FROM order1 o, user u WHERE o.userId = u.userId AND o.orderId = '52076fa9-433e-11e7-ab09-448a5b6dba5c' |
6.2.2. 修改订单POJO
如果查询结果中包含用户信息,就需要在Order.java pojo中增加User类型的属性,然后把用户信息保存在这个User属性中。
package cn.itcast.pojo;
/** * 订单信息POJO * * @author Derek Sun * */ public class Order {
private String orderId;
private Integer userId;
private String orderStatus;
private String goodsId;
private Date createDateTime;
private User user;
。。。。。。 } |
在实际业务中可能不仅仅包含用户信息,还可能有订单对应的商品信息、物流信息等等。
由于查询结果中需要包含一个User类型的对象,这样的结果集结构比较复杂,因此需要我们手动定义返回值映射,要用到ResultMap标签。
6.2.3. ResultMap中定义POJO对象
【OrderMapper.xml】
<说明>(需要掌握)
项目 |
解释 |
<association> |
用于在ResultMap标签中定义POJO子对象 |
property |
定义的POJO子对象名称(注意:名称不能随便命名,必须符合返回值映射规范) |
javaType |
定义的POJO子对象的类型 |
<SQL映射示例>
在ResultMap中我们需要告诉MyBatis,在结果集里面需要有一个User对象,并把属于用户的查询结果映射给User对象的属性。
<!-- 一对一查询结果集的返回 --> <!-- 定义订单综合查询结果与自定义订单pojo属性之间的对应关系 --> <resultMap type="cn.itcast.pojo.Order" id="orderResultMap1"> <id column="order_id" property="orderId" /> <result column="goods_id" property="goodsId" /> <result column="order_status" property="orderStatus" /> <association property="user" javaType="cn.itcast.pojo.User"> <result column="name" property="name"/> <result column="address" property="address"/> <result column="mobile" property="mobile"/> </association> </resultMap> <!-- 根据订单id查询订单综合信息(订单基本信息、所属用户信息...) --> <select id="findOrderAndUserByOrderId" parameterType="string" resultMap="orderResultMap1"> SELECT o.orderId, o.goodsId, o.orderStatus, u.name, u.address, u.mobile FROM order1 o, user u WHERE o.userId = u.userId AND o.orderId = #{id} </select> |
<SQL映射规范>
·返回值映射规范(四)
在<resultMap>中用<association>定义子对象,property属性放子对象名称,子对象名必须等于返回值映射类型中的属性名。(一对一结果的映射)
6.2.4. 定义接口
【OrderMapper.java】
// 根据订单id查询订单综合信息(订单基本信息、所属用户信息...) public Order findOrderAndUserByOrderId(String orderId) throws Exception; |
6.2.5. 客户端测试程序
【MyTest.java】
// 测试根据订单id查询订单综合信息(订单基本信息、所属用户信息...) @Test public void test1() throws Exception { SqlSession sqlSession = sqlSessionFactory.openSession(); // 创建DAO的动态代理对象 OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class); Order order = orderMapper.findOrderAndUserByOrderId("5f560c1e-433e-11e7-ab09-448a5b6dba5c"); System.out.println(order); sqlSession.close(); } |
6.2.6. 扩展
如果返回多条一对一的查询结果,该如何来做?
1. SQL
SELECT o.orderId, o.orderStatus, o.goodsId, u.userId, u.name, u.address FROM order1 o, user u WHERE o.userId = u.userId AND o.orderStatus = '02' |
2. POJO
同【Order.java】
3. 接口定义
// 根据订单状态查询订单综合信息(订单基本信息、所属用户信息...) public List<Order> selectOrderAndUserByOrderStatus(String orderStatus) throws Exception; |
4. 映射文件
<!-- 根据订单状态查询订单综合信息(订单基本信息、所属用户信息...) --> <select id="selectOrderAndUserByOrderStatus" parameterType="String" resultMap="customOrderResultType"> SELECT o.orderId, o.orderStatus, o.goodsId, u.userId, u.name, u.address FROM order1 o, user u WHERE o.userId = u.userId AND o.orderStatus = #{status} </select> |
5. 客户端测试
@Test public void test1() throws Exception { SqlSession sqlSession = sqlSessionFactory.openSession(); // 创建DAO的动态代理对象 OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class); List<CustomOrder> orderList = orderMapper.selectOrderAndUserByOrderStatus("02"); System.out.println(orderList); sqlSession.close(); } |
执行结果:
总结:无论是单条还是多条一对一结果,<resultMap> + <association>的组合都适用。association里面还可以再嵌套association。
6.3. 一对多查询结果集
6.3.1. SQL语句
SELECT u.name, u.address, u.mobile, o.order_id, o.order_status, o.goods_id FROM user u, order2 o WHERE o.user_id = u.userId AND u.userId = 1001 |
|
从SQL查询的结果集上看用户名和地址都是重复的,这个用户相关的订单信息是多条不同的,这样的结果集最终返回到java对象中应该是一个用户信息,其中包含一个关于这个用户的订单信息的List集合。
6.3.2. 修改用户POJO
如果查询用户结果中包含多个订单信息,就需要在User.java pojo中增加Order类型的List属性,然后把属于这个用户多条订单信息保存到List<Order>属性中。
package cn.itcast.pojo;
import java.util.List;
/** * 用户信息POJO * * @author Derek Sun * */ public class User {
private String name;
private int userId;
private String mobile;
private String sex;
private int age;
private String address;
// 用户订单信息列表 private List<Order> orderList;
。。。。。。。 }
|
在实际业务中可能不仅仅包含订单信息,还可能有用户信用度信息、用户消费额度信息、推荐商品信息列表等等。
由于查询结果中需要包含一个List<Order>类型的对象,这样的结果集结构比较复杂,因此需要我们手动定义返回值映射,要用到ResultMap标签。
需要在定义返回值映射时在ResultMap中定义一个List<Order>类型的对象,并把属于这个用户的订单查询结果映射给List<Order>对象。
6.3.3. ResultMap中定义List对象
【UserMapper.xml】
<说明>(需要掌握)
项目 |
解释 |
<collection> |
用于在ResultMap标签中定义List类型的子对象 |
property |
定义的List类型的子对象名称(注意:名称不能随便命名,必须符合返回值映射规范) |
ofType |
List中泛型的类型,即List其中一个对象的类型 |
<SQL映射示例>
在ResultMap中我们需要告诉MyBatis,在结果集里面需要有一个List<Order>对象,并把属于这个用户的订单都映射给List<Order>对象的属性。
<!-- 一对多查询结果集返回 --> <!-- 定义用户综合查询结果集与自定义用户pojo属性之间的对应关系 --> <resultMap type="cn.itcast.pojo.User" id="userResultMap"> <result column="name" property="name" /> <result column="address" property="address" /> <result column="mobile" property="mobile"/> <collection property="orderList" ofType="cn.itcast.pojo.Order"> <id column="order_id" property="orderId" /> <result column="order_status" property="orderStatus" /> <result column="goods_id" property="goodsId" /> </collection> </resultMap> <!-- 根据用户id查询用户综合信息(用户基本信息, 用户订单信息....) --> <select id="findUserAndOrderByUserId" parameterType="int" resultMap="userResultMap"> SELECT u.name, u.address, u.mobile, o.order_id, o.order_status, o.goods_id FROM user u, order2 o WHERE o.user_id = u.userId AND u.userId = #{id} </select> |
<SQL映射规范>
·返回值映射规范(五)
在<resultMap>中用<collection>定义集合子对象,property属性放集合子对象的名称,集合名必须等于返回值映射类型中的属性名。(一对多结果的映射)
6.3.4. 定义接口
【OrderMapper.java】
package cn.itcast.dao; import cn.itcast.pojo.User; public interface UserMapper { // 根据用户id查询用户综合信息(用户基本信息, 用户订单信息....) public User findUserAndOrderByUserId(Integer id) throws Exception; } |
6.3.5. 客户端测试程序
【MyTest.java】
// 测试根据用户id查询用户综合信息(用户基本信息, 用户订单信息....) @Test public void test1() throws Exception { SqlSession sqlSession = sqlSessionFactory.openSession(); // 创建DAO的动态代理对象 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); User user = userMapper.findUserAndOrderByUserId(1001); sqlSession.close(); } |
6.3.6. 扩展
如果返回多条一对多的查询结果,该如何来做?(多条一对多也可以理解成多对多,实际上多对多最终都是转化成一对多来实现的。)
1. SQL
SELECT u.userId, u.name, u.mobile, o.orderId, o.orderStatus, o.goodsId FROM user u, order1 o WHERE u.userId = o.userId AND u.name LIKE '王%' |
2. POJO
同【User.java】
3. 接口定义
// 根据用户名模糊查询用户综合信息(用户基本信息, 用户订单信息....) public List<User> selectUserAndOrderListByUserName(String userName) throws Exception; |
4. 映射文件
<!-- 根据用户名模糊查询用户综合信息(用户基本信息, 用户订单信息....) --> <select id="selectUserAndOrderListByUserName" parameterType="String" resultMap="userResultMap"> SELECT u.userId, u.name, u.mobile, o.orderId, o.orderStatus, o.goodsId FROM user u, order1 o WHERE u.userId = o.userId AND u.name LIKE #{userName} </select> |
5. 客户端测试
@Test public void test1() throws Exception { SqlSession sqlSession = sqlSessionFactory.openSession(); // 创建DAO的动态代理对象 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); List<User> userList = userMapper.selectUserAndOrderListByUserName("王%"); sqlSession.close(); } |
执行结果:
总结:无论是单条还是多条一对多结果,<resultMap> + <collection>的组合都适用。collection里面还可以再嵌套collection,但是一般没有那么复杂的数据结构。
7. 输入输出映射小结(重点)
<SQL映射规范>
·参数映射规范
1)传单个参数时,parameterType="简单类型",占位符的变量名可以任意,拼接符的变量名必须是value,但不能没有。
2)传多个参数时,parameterType="pojo类型",占位符或拼接符的变量名必须等于参数对象的属性名。
3)用包装类型传递参数时,parameterType="包装类型",占位符或拼接符的变量名等于包装类的属性.属性.属性...
4)如果参数映射是包装类中含有List属性, 那<foreach>标签中的collection必须等于List属性名。
5)如果参数映射直接是List类型,那<foreach>标签中的collection必须等于固定名称list(必须小写,不能变)。
·返回值映射规范
1)返回单值时,resultType="简单类型",值直接返回给java程序。
2)返回单/多条记录时 |
单:resultType="pojo类型" 多:resultType="pojo类型(集合泛型)" |
结果集列名必须等于pojo的属性名 |
3)在<resultMap>中用<association>定义子对象,property属性放子对象名称,子对象名必须等于返回值映射类型中的属性名。(一对一结果的映射)
4)在<resultMap>中用<collection>定义集合子对象,property属性放集合名称,集合名必须等于返回值映射类型中的属性名。(一对多结果的映射)
8. Spring与Mybatis整合
框架的整合就是软件之间的集成,它很抽象,因此在做整合之前先想好思路。规划好思路然后按照思路一步一步的做就可以实现框架的整合。
8.1. SM整合思路
8.1.1. 思路分析
SM整合是中后端框架的整合:
Spring框架作为一个优秀的容器级框架在系统整合中一直充当着业务控制中心的作用。是后端的核心。管理着系统中重要的资源、重要的核心类的对象、业务层对象以及持久层Dao对象。
MyBatis作为一个优秀的持久层框架,其优势当然是访问数据库的具体工作,所以交出对重要资源和核心类对象的控制权,专心做好对数据库访问的具体工作。
分析后得出下面的思路。
8.1.2. 思路整理
1. 控制层
框架:由Spring负责重要的资源、核心类对象、DAO对象的管理。
配置:ApplicationContext.xml
——properties属性文件的载入(可选,推荐)
——数据源以及连接池(必选)
——MyBatis会话工厂bean(必须)
——传统Dao实现类的bean(可选)
——MyBatis动态代理Dao的bean(可选)
——MyBatis动态代理Dao的包扫描
(批量加载bean)(可选,推荐)
2. 持久层
框架:由MyBatis负责访问数据库的具体工作,即通过映射文件中的SQL进行数据库操作。
配置:MyBatisConfig.xml(配置文件名称随意,有的人叫SqlMapConfig.xml)
——自定义别名配置(可选,不推荐)
——传统DAO开发方式需要的映射文件配置(可选,看有没有传统DAO的情况而定。)
8.2. Jar包
8.2.1. Spring的jar包
如何从Spring官网取得jar包
1 访问: http://projects.spring.io/spring-framework/
点击[Reference]进入后, 搜索[Distribution Zip Files]
下面的才是spring的jar包下载的页面,对应的url:http://repo.spring.io/release/org/springframework/spring/
以下是取得的jar包
8.2.2. Mybatis的jar包
8.2.3. 数据库驱动jar
8.2.4. SM集成的jar
Spring与MyBatis的集成需要借助这个集成jar包才能把MyBatis的会话工厂、DAO的对象注入到spring的容器中去,没有它我们完不成SM的整合。
Spring与Mybatis整合包的取得地址: https://github.com/mybatis/spring/releases
以下是我们使用的sm整合jar包:
8.2.5. SM整合的其他依赖jar
以下是sm整合的其他依赖包
8.3. 创建工程(UTF-8)、导入jar、创建配置文件
其中ApplicationContext.xml是空的,下面在具体添加内容,其他配置文件是之前的工程中拷贝过来的。
8.4. SM环境整合
8.4.1. 规划要进行配置的内容
根据上面的思路可知我们今天的整合主要面对的是两个配置文件:
ApplicationContext.xml
MyBatisConfig.xml
因为只是SM整合,还不是一个完整的系统,我们没有太多的代码可写,只是测试一下我们前面写在映射文件中的SQL在整合环境下好不好用即可。
8.4.2. ApplicationContext.xml
Spring配置样本可以从官网取得: http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#jdbc
从官网取得的文件:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="corporateEventDao" class="com.example.JdbcCorporateEventDao"> <property name="dataSource" ref="dataSource"/> </bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean>
<context:property-placeholder location="jdbc.properties"/>
</beans> |
8.4.3. 数据源及属性文件的配置
<!-- 1 properties属性文件的载入 --> <context:property-placeholder location="jdbc.properties"/> <!-- 2 数据源以及连接池配置 --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> |
基本和官网获取到的一样,只是属性文件的key名稍作修改。
8.4.4. Mybatis会话工厂bean的配置(重点)
<说明>
项目 |
解释 |
核心类 |
org.mybatis.spring.SqlSessionFactoryBean |
configLocation |
核心类重要属性名,用value加载MyBatis核心配置文件 |
dataSource |
核心类重要属性名,用ref引用数据源的id |
<配置>
<!-- 3. 配置会话工厂 --> <!-- 配置会话工程 --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 加载MyBatis核心配置文件 --> <property name="configLocation" value="MyBatisConfig.xml" /> <!-- 引用数据源bean,注意使用ref,属性名不要写错 --> <property name="dataSource" ref="dataSource" /> </bean> |
8.4.5. 将MyBatis的核心配置文件中的配置删除
删除属性文件配置、数据库环境配置,如果有传统DAO,则需保留传统DAO需要使用的这个映射文件。
<?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>
<!-- 自定义java bean的别名定义 --> <!-- <typeAliases> <typeAlias type="cn.itcast.pojo.User" alias="user"/> </typeAliases> -->
<!-- 配置映射文件 --> <mappers> <!-- 方式一: 根据映射文件在类目录下的相对路径加载映射文件 --> <mapper resource="cn/itcast/mapper/UserMapper.xml" /> </mappers>
</configuration> |
8.5. 传统DAO实现类的bean(重点)
<说明>
项目 |
解释 |
DAO实现类要继承的父类 |
org.mybatis.spring.support.SqlSessionDaoSupport |
继承父类的作用 |
由父类统一管理SqlSession对象的打开、关闭,DAO实现类方法只关注业务。 |
sqlSessionFactory |
父类需要的重要属性名,用ref引用会话工厂的id |
<配置>
<!-- 4. 配置传统DAO实现类的bean --> <!-- 配置DAO接口的实例化bean(使用DAO接口的实现类) --> <bean id="userDao" class="cn.itcast.dao.UserDaoImpl"> <!-- 引用会话工厂bean,注意使用ref,属性名不要写错 --> <property name="sqlSessionFactory" ref="sqlSessionFactory" /> </bean> |
DAO接口【UserDao.java】
package cn.itcast.dao;
import cn.itcast.pojo.User;
public interface UserDao { // 根据id查询用户信息 public User findUserById(Integer id) throws Exception; } |
DAO实现类【UserDaoImpl.java】
package cn.itcast.dao;
import org.apache.ibatis.session.SqlSession; import org.mybatis.spring.support.SqlSessionDaoSupport;
import cn.itcast.pojo.User;
public class UserDaoImpl extends SqlSessionDaoSupport implements UserDao {
@Override public User findUserById(Integer id) throws Exception { // DAO接口实现类的方法中通过方法getSqlSession来取得sqlSession对象。 SqlSession sqlSession = this.getSqlSession(); // 根据id查询 User userInfo = sqlSession.selectOne("user.selectUserById", id); return userInfo; } } |
这里面没有关闭sqlSession的代码,如果加上会报错,告诉你不需要关闭,由Spring会帮你做。
测试【MyTest.java】
package sm;
import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;
import cn.itcast.dao.UserDaoImpl; import cn.itcast.mapper2.OrderMapper; import cn.itcast.pojo.Order; import cn.itcast.pojo.User;
public class MyTest { @Test public void test1() throws Exception { ApplicationContext ac = new ClassPathXmlApplicationContext("ApplicationContext.xml"); UserDao userDao = (UserDao)ac.getBean("userDao"); User userInfo = userDao.findUserById(1001); System.out.println(userInfo); } } |
8.6. MyBatis动态代理DAO的bean(重点)
<说明>
项目 |
解释 |
核心类 |
org.mybatis.spring.mapper.MapperFactoryBean |
核心类作用 |
这个核心类帮助我们动态代理指定的DAO接口,并加载对应的映射文件,所以二者要同名同目录 所以这种DAO加载方式只适用于MyBatis动态代理DAO开发方式。 |
mapperInterface |
核心类重要属性,value的值表示需要动态代理的接口的完全限定名 |
sqlSessionFactory |
核心类中也继承了org.mybatis.spring.support.SqlSessionDaoSupport,这个父类需要会话工厂对象,用ref引用会话工厂的id |
<配置>
<!-- 5. 配置MyBatis动态代理DAO的bean --> <bean id="orderDao" class="org.mybatis.spring.mapper.MapperFactoryBean"> <!-- 设置需要代理的DAO接口 --> <property name="mapperInterface" value="cn.itcast.mapper2.OrderMapper" /> <!— 因为MapperFactoryBean继承了SqlSessionDaoSupport,所以需要给它传递会话工程bean --> <property name="sqlSessionFactory" ref="sqlSessionFactory" /> </bean> |
DAO接口【OrderMapper.java】
package cn.itcast.mapper2;
import cn.itcast.pojo.Order;
public interface OrderMapper { // 根据id查询订单信息2 public Order findOrder2ById2(String orderId) throws Exception; } |
测试【MyTest.java】
package sm;
import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;
import cn.itcast.dao.UserDaoImpl; import cn.itcast.mapper2.OrderMapper; import cn.itcast.pojo.Order; import cn.itcast.pojo.User;
public class MyTest { @Test public void test2() throws Exception { ApplicationContext ac = new ClassPathXmlApplicationContext("ApplicationContext.xml"); OrderMapper orderMapper = (OrderMapper)ac.getBean("orderDao"); Order orderInfo = orderMapper.findOrder2ById2("57528381-433e-11e7-ab09-448a5b6dba5c"); System.out.println(orderInfo); } } |
8.7. MyBatis动态代理DAO的包扫描(重点)
这是真正企业中DAO动态代理的配置方式(注意:要将第五个配置先注释掉,才能测试第六个配置)
<说明>
项目 |
解释 |
核心类 |
org.mybatis.spring.mapper.MapperScannerConfigurer |
核心类作用 |
这个核心类帮助我们批量动态代理指定包下的DAO接口,并加载对应的映射文件,是上面的批量形式,所以二者要同名同目录。 所以这种DAO加载方式只适用于MyBatis动态代理DAO开发方式。 |
basePackage |
核心类重要属性名,用value加载需要扫描的包路径,需要多个包时,中间用半角逗号隔开 |
·注意:因为是包扫描,所以无法指定具体的id,只需class即可。而且这个核心类不需要配置会话工厂,它会自动去找Spring容器中注册的会话工厂对象。
<!-- 6. 配置MyBatis动态代理DAO的包扫描 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="cn.itcast.mapper2" /> </bean> |
因为没知道bean的id,那我们如何在程序中引用这些被加载的DAO呢?
程序中需要使用哪个DAO接口时,用DAO接口名字首字母小写作为它的id即可。若接口名字开头字母有两个及以上字母是大写的(如:TOrderMapper),那么就直接用DAO接口名字作为id。
DAO接口【OrderMapper.java】同8.6。
测试【MyTest.java】
@Test public void test3() throws Exception { ApplicationContext ac = new ClassPathXmlApplicationContext("ApplicationContext.xml"); OrderMapper orderMapper = (OrderMapper)ac.getBean("orderMapper"); Order orderInfo = orderMapper.findOrderById2("57528381-433e-11e7-ab09-448a5b6dba5c"); System.out.println(orderInfo); } |
9. Mybatis逆向工程
1. 能够帮助我们生成繁琐又没有技术含量的POJO的代码。
2. 还能帮助我们生成单表的增删改查的SQL映射文件以及动态代理接口。
之所以能帮我们生成单表的增删改查的处理是因为单表的这些操作是跟业务无关的,而且它生成的这些SQL中的条件都是全条件的(即对全部字段都可以作为条件,根据传递的是哪些字段的条件动态的生成SQL。)
9.1. 将逆向工程导入workspace
9.2. 逆向工程构成
1 一个配置文件:generatorConfig.xml
2 三个jar包:
MyBatis核心jar:mybatis-3.2.7.jar
逆向工程的jar:mybatis-generator-core-1.3.2.jar
数据库驱动:mysql-connector-java-5.1.28-bin.jar
3 一个main函数方法,直接执行可以上传代码
以上内容均不需要我们记住里面的内容,就把整个工程看作一个工具,是官方提供给我们的工具。
9.3. 生成代码
直接运行MyGenerator.java生成代码
注意:每次运行前都要将原来生成的代码删除后在运行生成新的代码,如果不先删除,新生成的代码并不会自动覆盖原来的代码文件而是在原来的代码文件中追加一些不可见的字符导致文件损坏,因此必须先删除在重新生成。
9.4. 结合当前工程的逆向工程详细代码
黄色部分为重要的修改项:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration> <context id="testTables" targetRuntime="MyBatis3"> <commentGenerator> <!-- 是否去除自动生成的注释 true:是 : false:否 --> <property name="suppressAllComments" value="true" /> </commentGenerator> <!--数据库连接的信息:驱动类、连接地址、用户名、密码 --> <jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/mybatis" userId="root" password="123"> </jdbcConnection> <!-- <jdbcConnection driverClass="oracle.jdbc.OracleDriver" connectionURL="jdbc:oracle:thin:@127.0.0.1:1521:yycg" userId="yycg" password="yycg"> </jdbcConnection> -->
<!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL 和 NUMERIC 类型解析为java.math.BigDecimal --> <javaTypeResolver> <property name="forceBigDecimals" value="false" /> </javaTypeResolver>
<!-- targetProject:生成PO类的位置 --> <javaModelGenerator targetPackage="cn.itcast.pojo2" targetProject=".\src"> <!-- enableSubPackages:是否让schema作为包的后缀 --> <property name="enableSubPackages" value="false" /> <!-- 从数据库返回的值被清理前后的空格 --> <property name="trimStrings" value="true" /> </javaModelGenerator> <!-- targetProject:mapper映射文件生成的位置 --> <sqlMapGenerator targetPackage="cn.itcast.mapper2" targetProject=".\src"> <!-- enableSubPackages:是否让schema作为包的后缀 --> <property name="enableSubPackages" value="false" /> </sqlMapGenerator> <!-- targetPackage:mapper接口生成的位置 --> <javaClientGenerator type="XMLMAPPER" targetPackage="cn.itcast.mapper2" targetProject=".\src"> <!-- enableSubPackages:是否让schema作为包的后缀 --> <property name="enableSubPackages" value="false" /> </javaClientGenerator> <!-- 指定数据库表 --> <table schema="" tableName="user"></table> <table schema="" tableName="order1"></table> <table schema="" tableName="order2"></table> <!-- <table schema="" tableName="user"></table> -->
<!-- 有些表的字段需要指定java类型 <table schema="" tableName=""> <columnOverride column="" javaType="" /> </table> --> </context> </generatorConfiguration> |
生成的代码:
把这些代码连同包一起拷贝到sm工程中去测试:
在ApplicationContext.xml的包扫描动态代理配置中追加新的包路径
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!-- 设置扫描的包路径,如果需要扫描多个包,中间用半角逗号隔开 --> <property name="basePackage" value="cn.itcast.mapper,cn.itcast.mapper2" /> </bean> |
在【MyTest.java】中添加测试方法
@Test public void test4() throws Exception { ApplicationContext ac = new ClassPathXmlApplicationContext("ApplicationContext.xml"); // 创建接口的动态代理对象 Order1Mapper order1Mapper = (Order1Mapper)ac.getBean("order1Mapper"); // 调用逆向工程生成的接口方法,根据主键查询订单信息 Order1 order1 = order1Mapper.selectByPrimaryKey("57528381-433e-11e7-ab09-448a5b6dba5c"); System.out.println(order1.getOrderstatus());
// 调用多条件设置的对象设置条件,然后执行查询 Order1Example example = new Order1Example(); Criteria criteria = example.createCriteria(); List<String> statusList = new ArrayList<String>(); statusList.add("01"); statusList.add("02"); criteria.andOrderstatusIn(statusList); criteria.andUseridEqualTo(1001); List<Order1> orderList = order1Mapper.selectByExample(example); System.out.println(orderList.size()); } |
9.5. MyBatis逆向工程的好处
1. 快速生成每个表对应的pojo,省去编码工作。
2. 快速生成单表的增删改查的操作接口和对应的映射文件,单表的增删改查处理也不需要手动编码了。
9.6. MyBatis逆向工程注意事项
1. 每次在重新生成前要把原先生成的代码删除,然后在重新生成。注意不能覆盖,因为覆盖会使覆盖后的文件中生成一些不可见的字符,这些字符你在eclipse中打开是看不到有任何错误的,但一运行就会出错,原因就是这些不可见字符损坏了文件。所以每次生成前要先删除原先生成的所有代码和包目录。
2. 逆向工程只能生成单表的增删改查操作的接口,对于多表关联处理还需要人手工编写。