SSM-MyBatis(二)-动态SQL、druid连接池、配置文件、分页查询
动态SQL
- 什么是动态SQL?
- 多条件查询,当满足n个条件时,查询数据库
if标签
-
通常用来做判断,判断根据判断是否添加某段sql语句
//skill.xml <select id="dynamicSQL" parameterType="Map" resultType="com.zh.bean.Skill"> SELECT * FROM skill WHERE 1 = 1 <if test="id != null"> AND id > #{id} </if> <if test="name != null"> AND name LIKE #{name} </if> <if test="level != null"> AND level < #{level} </if> </select>
-
SkillTest测试代码
public void dynamicSQL() throws Exception { try (SqlSession session = MyBatises.openSession()) { Map<String, Object> param = new HashMap<>(); param.put("id", 3); param.put("name", "%s%"); param.put("level", 900); /* SELECT * FROM skill WHERE 1 = 1 AND id > #{id} AND name LIKE #{name} AND level < #{level} */ List<Skill> skills = session.selectList("skill.dynamicSQL2", param); for (Skill skill : skills) { System.out.println(skill); } } }
where标签
//skill.xml代码
<select id="dynamicSQL2" parameterType="Map" resultType="com.zh.bean.Skill">
SELECT * FROM skill
<where>
<if test="id != null">
AND id > #{id}
</if>
<if test="name != null">
AND name LIKE #{name}
</if>
<if test="level != null">
AND level < #{level}
</if>
</where>
</select>
- 与上面相比,不用在WHERE后面添加
1=1
了,之前添加目的是因为后序拼接的语句会多余一个AND - 如果用了where会自动删除多余的AND
批量处理
1.批量添加
-
skill.xml
<insert id="batchInsert" useGeneratedKeys="true" keyProperty="id" parameterType="List"> INSERT INTO skill(name, level) VALUES <!-- 要遍历的集合collection、skill:集合中的元素、separator之间用什么分割--> <foreach collection="list" item="skill" separator=","> (#{skill.name}, #{skill.level}) </foreach> </insert>
-
SkillTest测试代码
@Test public void batchInsert() throws Exception { try (SqlSession session = MyBatises.openSession()) { List<Skill> skills = new ArrayList<>(); skills.add(new Skill("Java1", 111)); skills.add(new Skill("Java2", 222)); skills.add(new Skill("Java3", 333)); skills.add(new Skill("Java4", 444)); session.insert("skill.batchInsert", skills); //获取插入的所有id for (Skill skill : skills) { System.out.println(skill.getId()); } session.commit(); } }
2.批量删除
-
skill.xml(2种写法)
<delete id="batchDelete" parameterType="List"> DELETE FROM skill WHERE id IN ( <foreach collection="list" item="id" separator=","> #{id} </foreach> ) </delete> <delete id="batchDelete2" parameterType="List"> DELETE FROM skill WHERE id IN <foreach collection="array" item="id" open="(" close=")" separator=","> #{id} </foreach> </delete>
-
SkillTest测试代码
@Test public void batchDelete() throws Exception { try (SqlSession session = MyBatises.openSession()) { //传参为List,collection需要设置为list // List<Integer> ids = new ArrayList<>(); // ids.add(23); // ids.add(24); // ids.add(25); // ids.add(26); //传参为数组,collection需要设置为array Integer[] ids = {7, 8, 9}; session.insert("skill.batchDelete2", ids); session.commit(); } }
3.注意事项
- 批量添加的效率比【多次单个添加】要高,但是它无法使用
<selectedKey>
获取新插入记录的主键- 但可以使用useGeneratedKeys获取主键
- useGeneratedKeys:代表使用自动生成的主键
- keyProperty:告诉主键属性对应的bean对象的那个属性名称
- 批量操作生成的SQL语句可能会比较长,有可能会超过数据库的限制
- 如果传进来的参数是List,collection属性值为list就可以遍历这个List
- 如果传进来的参数是数组,collection属性值为array就可以遍历这个数组
其他标签
typeAliases
- 用于设置类型的别名(不区分大小写)
- 添加到mybatis-config.xml的configuration标签中
- 注意添加在settings标签与environments标签之间
<!-- 别名 --> <typeAliases> <!-- 一旦设置了别名,它是不区分大小写的 --> <!-- 给这个类com.zh.bean.Skill设置一个别名,以后所有的bean映射文件中所有出现这个类的地方都可以换成skill--> <typeAlias type="com.zh.bean.Skill" alias="skill" /> <!-- 这个包下的所有类,都会起一个别名:全类名的最后一个单词 这么一设置,等于给com.zh.bean包下的所有类都设置了一个别名,别名名称为最后一个单词 比如com.zh.bean.Skill的别名就是Skill --> <package name="com.zh.bean" /> </typeAliases>
-
那么之前skill.xml的查询语句可以这么写
//resultType不用再写com.zh.bean.Skill,直接写skill即可,SKILL/SkiLL...都可以 <select id="list" resultType="skill"> SELECT * FROM skill </select>
- 参考:https://mybatis.org/mybatis-3/zh/configuration.html#typeAliases
- 注意: 那么我们可以猜测mybatis-config.xml文件中所有type的值看起来不像类的应该都是别名,比如POOLED、JDBC等,只是mybatis自己内部定义的别名
sql标签
- 添加到mapper标签中,用于抽取公共的SQL语句
- 比如skill.xml中有的SQL语句很多这条语
SELECT * FROM skill
-
那么抽取如下
<sql id="sqlListAll"> SELECT * FROM skill </sql>
-
其他的语句如下
<select id="list2" parameterType="com.zh.bean.Skill" resultType="com.zh.bean.Skill"> <include refid="sqlListAll" /> WHERE id > #{id} OR level > #{level} OR name LIKE #{name} </select>
集成druid连接池
-
mybatis-config.xml数据库配置如下
<environment id="production"> <!--使用JDBC来管理事务--> <transactionManager type="JDBC"/> <!--POOLED 采用连接池的方式管理数据库连接 --> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/test-mybatis"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment>
- 只要将dataSource的type的值设置为POOLED,那么就使用了mybatis内置的连接池
- 也可以通过property设置内置连接池的常用属性
-
但是通常不使用mybatis内置的连接池,通常使用其他第三方封装的连接池更好用,那么如何使用呢?比如比较流行的连接池druid
使用步骤
-
在pom.xml添加maven依赖导入druid
<dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.3</version> </dependency>
-
新建一个继承UnpooledDataSourceFactory的连接池类(com.zh.common.DruidDataSourceFactory),在构造方法中设置数据源
package com.zh.common; import com.alibaba.druid.pool.DruidDataSource; import org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory; public class DruidDataSourceFactory extends UnpooledDataSourceFactory { public DruidDataSourceFactory() { this.dataSource = new DruidDataSource(); } }
- mybatis要求第三方如果想要替换自己的连接池,必须实现UnpooledDataSourceFactory类,而且实现DruidDataSourceFactory构造方法,并返回第三方的连接池数据源方法
-
在environment标签中配置连接池的类名
<environment id="development1"> <!-- 采用JDBC的事务管理方法 --> <transactionManager type="jdbc" /> <!-- type为设置其他的连接池对象 --> <!-- <dataSource type="com.zh.common.DruidDataSourceFactory"> --> <!-- 可以设置一个别名DRUID,取代com.zh.common.DruidDataSourceFactory--> <dataSource type="DRUID"> <!-- 这里面的值跟之前JAVAEE项目中的druid.properties文件夹中的一样 --> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/test_mybatis"/> <property name="username" value="root"/> <property name="password" value="root"/> <property name="initialSize" value="5"/> <property name="maxActive" value="10"/> <property name="maxWait" value="5000"/> </dataSource> </environment>
原理分析
- mybatis会根据dataSource标签设置的连接池类型,创建那个对象
- 然后去读取property标签设置的所有属性
- 然后将这些属性值设置给第一步创建的对象
数据库信息可配置化
- 上面的配置将数据库以及连接池的信息直接写死在environment标签中了,那么是否可跟之前一样可配置化呢?
-
在resources中导入druid.properties文件
//测试 dev.driverClass=com.mysql.jdbc.Driver dev.url=jdbc:mysql://localhost:3306/test-mybatis dev.username=root dev.password=root dev.initialSize=5 dev.maxActive=10 dev.maxWait=5000 //生产 pdr.driverClass=com.mysql.jdbc.Driver pdr.url=jdbc:mysql://localhost:3306/xr pdr.username=root pdr.password=root pdr.initialSize=5 pdr.maxActive=10 pdr.maxWait=500
-
在mybatis-config.xml的configuration标签中添加导入druid.properties文件
<!-- 这样写就相当于把druid.properties文件中所有的变量导入--> <properties resource="druid.properties" />
-
environments动态设置
<!-- 开发环境(调试阶段) --> <environment id="development"> <!-- 采用JDBC的事务管理方法 --> <transactionManager type="JDBC" /> <!-- DRUID是别名 --> <dataSource type="DRUID"> <!-- 值都是动态设置的 --> <property name="driverClass" value="${dev.driverClass}"/> <property name="url" value="${dev.url}"/> <property name="username" value="${dev.username}"/> <property name="password" value="${dev.password}"/> <property name="initialSize" value="${dev.initialSize}"/> <property name="maxActive" value="${dev.maxActive}"/> <property name="maxWait" value="${dev.maxWait}"/> </dataSource>
PageHelper-分页查询
- 数据库的分页查询
- 不同数据库的分页查询SQL可能是不一样的,比如:每页10条数据,查询第2页(第11-20条记录)
#MySQL,编号从0开始,所以第二页从10开始 SELECT * FROM student LIMIT 10,10; #SQL Server 微软提供 SELECT * FROM student OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY; #Oracle SELECT * FROM (SELECT s.*, ROWNUM rn FROM (SELECT *FROM student) S WHERE ROWNUM <= 20 ) WHERE rn>= 11;
- 所以,在MyBatis中使用不同的数据库,就需要编写不同的分页查询SQL
PageHelper简介
- PageHelper是由国人开发的MyBatis分页插件,支持多种常用的数据库,可以极大简化分页的业务逻辑
- 源码:https://github.com/pagehelper/Mybatis-PageHelper
- 使用方法:https://github.com/pagehelper/Mybatis-PageHelper/blob/master/wikis/zh/HowToUse.md
使用步骤
- 环境配置
-
首先要增加Maven依赖
<dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.2.0</version> </dependency>
-
在mybatis-config.xml中配置插件
<!-- 注意plugins标签添加在typeAlias与environments之间--> <plugins> <plugin interceptor="com.github.pagehelper.PageInterceptor"> <!-- true代表分页合理化: pageNum <= 0 就会自动获取第1页,如果pageNum>pages时就会自动获取最后1页 --> <property name="reasonable" value="true"/> </plugin> </plugins>
-
reasonable设置true
- pageNum <= 0 时会查询第一页,pageNum > pages(超过总页数时),会查询最后一页
- pageNum:查询的页码
-
- 代码使用
-
skill.xml中查询语句没有任何变化,PageHelper会自动拼接查询语句
<select id="list" resultType="com.zh.bean.Skill"> SELECT * FROM skill </select>
-
SkillTest中测试代码如下:
@Test public void page() throws Exception { try (SqlSession session = MyBatises.openSession()) { //第三页开始,每页5条数据,不需要管是什么类型数据库,也不用写分页查询SQL语句 PageHelper.startPage(3, 5); List<Skill> skills = session.selectList("skill.list"); for (Skill skill : skills) { System.out.println(skill); } } }
-