SSM学习笔记之MyBatis
0x00 概述
本文涉及SSM框架中MyBatis知识点,详细见目录。
0x01 MyBatis介绍
MyBatis是一个半自动的ORM框架,你给sql,我来执行。
ORM(Object Relation Mapping)对象关系映射,将Java中的一个对象与数据表中的一行记录一一对应。
ORM框架提供了实体类与数据表的映射关系,通过映射文件的配置,将文件保存到了数据表中,实现了对象的持久化。
MyBatis特点:
- 支持自定义sql,存储过程
- 对原有的JDBC进行了封装,几乎消除了所有JDBC代码(打开连接,执行sql等),让开发者专注sql
- 支持XML和注解配置(@xxxx)的方式自动完成ORM操作,实现结果映射,对象的属性和数据表的对应关系就叫映射
0x02 MyBatis框架部署
2.1 创建Maven项目
- java工程
- Web工程
2.2 在项目中添加MyBatis依赖
- 在pom.xml中添加依赖--mybatis和mysql driver
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.6</version> </dependency>
说明,在pom.xml中添加包的说明以后,需要到Maven中刷新一下,将包安装进来
2.3 创建MyBatis配置文件,配置数据库连接
需要先定义MyBatis配置模版,其他的模版也可以在这里进行配置
选择resources--右键New--Edit File Templates
<?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>
</configuration>
在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> <!-- environments配置数据库连接信息 --> <!-- environments中可以定义多个environments标签(正式测试预发布),每个environments可以定义一套数据库连接配置--> <!-- default属性,用来执行使用哪个environment 标签,--> <environments default="mysql"> <environment id="mysql"> <!-- transactionManager用于配置数据库管理方法 --> <transactionManager type="JDBC"></transactionManager> <!-- dataSource标签用来配置数据库连接信息--> <dataSource type="POOLED"> <property name="driver" value="com.mysql"/> <property name="url" value="jdbc:mysql://localhost:3306/jdbcpractice?characterEncoding = utf-8"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> </configuration>
0x03 MyBatis框架使用
案例:学生信息的数据库操作
3.1 创建数据表
CREATE TABLE tb_students( sid int PRIMARY key auto_increment, stu_num CHAR(5) not null UNIQUE, stu_name VARCHAR(20) not null, sut_gender CHAR(2) not null, stu_age int not null );
3.2 创建实体类
导入lombok,然后添加注释
package com.MyBatisDemo.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.ToString; @Data @AllArgsConstructor @NoArgsConstructor @ToString public class Student { private int stuId; private String stuNum; private String stuName; private String stuGender; private int stuAge; }
3.3 创建DAO接口,并创建操作方法
package com.MyBatisDemo.dao; import com.MyBatisDemo.pojo.Student; public interface StudentDAO { public int insertStudent(Student student); public int deleteStudent(Student stuNum); }
3.4 创建DAO接口的映射文件
模版:
<?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=""> </mapper>
在resources目录下,新建mappers文件夹
在mappers中新建StudentMapper.xml文件,这个可以根据上面的模版进行创建,注意这里的StudentMapper名称建议和DAO联系起来
在映射文件中,对DAO中的方法进行实现
<?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文件相当于DAO接口的实现类,namespace属性要指定实现DAO接口的全限定名(全限定名:包名加类名)--> <!-- copy reference--> <mapper namespace="com.MyBatisDemo.dao.StudentDAO"> <!--parameterType 这个参数在DAO中已经指定类型了,在这里可以省略--> <insert id="insertStudent" parameterType="com.MyBatisDemo.pojo.Student"> insert into tb_student(stu_num,stu_name,stu_gender,stu_age) values(#{stuNum},#{stuName},#{stuGender},#{stuAge}) </insert> <delete id="deleteStudent"> delete from tb_student where stu_num = #{stuNum} </delete> </mapper>
3.5 将映射文件添加到主配置文件
<?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> <!-- environments配置数据库连接信息 --> <!-- environments中可以定义多个environments标签(正式测试预发布),每个environments可以定义一套数据库连接配置--> <!-- default属性,用来执行使用哪个environment 标签,--> <environments default="mysql"> <environment id="mysql"> <!-- transactionManager用于配置数据库管理方法 --> <transactionManager type="JDBC"></transactionManager> <!-- dataSource标签用来配置数据库连接信息--> <dataSource type="POOLED"> <property name="driver" value="com.mysql"/> <property name="url" value="jdbc:mysql://localhost:3306/jdbcpractice?characterEncoding = utf-8"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <mappers> <mapper resource="mappers/StudentMapper.xml"></mapper> </mappers> </configuration>
0x04 单元测试
测试类往往是在测试类名后加Test,测试方法往往是在测试方法名前加Test
4.1 添加单元测试依赖
junit
4.2 创建测试类
在被测试接口类名后面, alt+insert --> test
package com.MyBatisDemo.dao; import com.MyBatisDemo.pojo.Student; 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.Test; import javax.annotation.Resource; import java.io.IOException; import java.io.InputStream; import static org.junit.Assert.*; public class StudentDAOTest { @Test public void insertStudent() { try { //加载mybatis配置文件 InputStream is = Resources.getResourceAsStream("mybatis-config.xml"); //创建builder SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); //会话工厂,连接工厂 SqlSessionFactory factory = builder.build(is); //sqlsession 代表数据库的连接,也代表数据库的连接对象 //会话(连接) SqlSession sqlSession = factory.openSession(); StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class); // System.out.println(studentDAO); int i = studentDAO.insertStudent(new Student(0,"10002","java少年","男",24)); //需要手动提交 sqlSession.commit(); System.out.println(i); } catch (IOException e) { e.printStackTrace(); } } @org.junit.Test public void deleteStudent() { } }
0x05 MyBatisCRUD操作
案例:学生信息的增删改查
5.1 添加操作
略
5.2 删除操作
根据学号删除一条学生信息
步骤:
- 在StudentDAO中定义删除方法
package com.MyBatisDemo.dao; import com.MyBatisDemo.pojo.Student; public interface StudentDAO { public int insertStudent(Student student); public int deleteStudent(String stuNum); }
- 在StudentMapper.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文件相当于DAO接口的实现类,namespace属性要指定实现DAO接口的全限定名(全限定名:包名加类名)--> <!-- copy reference--> <mapper namespace="com.MyBatisDemo.dao.StudentDAO"> <!--parameterType 这个参数在DAO中已经指定类型了,在这里可以省略--> <insert id="insertStudent" parameterType="com.MyBatisDemo.pojo.Student"> insert into tb_students(stu_num,stu_name,stu_gender,stu_age) values(#{stuNum},#{stuName},#{stuGender},#{stuAge}) </insert> <delete id="deleteStudent"> delete from tb_students where stu_num = #{stuNum} </delete> </mapper>
- 测试:在StudentDAO的测试类中添加测试方法
package com.MyBatisDemo.dao; import com.MyBatisDemo.pojo.Student; 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.Test; import javax.annotation.Resource; import java.io.IOException; import java.io.InputStream; import static org.junit.Assert.*; public class StudentDAOTest { @Test public void insertStudent() { try { //加载mybatis配置文件 InputStream is = Resources.getResourceAsStream("mybatis-config.xml"); //创建builder SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); //会话工厂,连接工厂 SqlSessionFactory factory = builder.build(is); //sqlsession 代表数据库的连接,也代表数据库的连接对象 //会话(连接) SqlSession sqlSession = factory.openSession(); StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class); // System.out.println(studentDAO); int i = studentDAO.insertStudent(new Student(0,"10002","java少年","男",24)); //需要手动提交 sqlSession.commit(); System.out.println(i); } catch (IOException e) { e.printStackTrace(); } } @Test public void testDeleteStudent() { try { InputStream is = Resources.getResourceAsStream("mybatis-config.xml"); SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); //SqlSessionFactory表示mybatis的会话工厂,工厂模式 SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is); //SqlSession 对象是mybatis和数据库之间的连接,也就是会话,创建了连接,可以得到所有的mapper对象(映射关系) SqlSession sqlSession = sqlSessionFactory.openSession(); //通过SqlSession 对象调用getMapper方法获取DAO接口对象 StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class); //调用被测试方法 int i = studentDAO.deleteStudent("10001"); //提交事务 sqlSession.commit(); System.out.println(i); } catch (IOException e) { e.printStackTrace(); } } }
5.3 修改操作
根据学号,修改其他字段信息
@Test public void testUpdateStudent(){ try { InputStream is = Resources.getResourceAsStream("mybatis-config.xml"); SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); SqlSessionFactory sqlSessionFactory = builder.build(is); SqlSession sqlSession = sqlSessionFactory.openSession(); StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class); int i = studentDAO.updateStudent(new Student(3,"10002",".net小小少年","男",25)); sqlSession.commit(); assertEquals(1,i);//期望,单元测试的期望返回结果 System.out.println(i); } catch (IOException e) { e.printStackTrace(); } }
5.4 查询操作--查询所有
package com.MyBatisDemo.dao; import com.MyBatisDemo.pojo.Student; import java.util.List; public interface StudentDAO { public int insertStudent(Student student); public int deleteStudent(String stuNum); public int updateStudent(Student student); public List<Student> listStudents(); }
<!-- resultType执行查询结果,封装的对象的实体类--> <!-- resultSets指定当前操作返回的集合类型(可省略)--> <!-- resultType返回的类型--> <!-- <select id="listStudents" resultType="com.liguoqing.pojo.Student" resultSets="java.util.List">--> <!-- select--> <!-- sid as stuId,--> <!-- stu_num as stuNum,--> <!-- stu_name as stuName,--> <!-- stu_gender as stuGender,--> <!-- stu_age as stuAge--> <!-- from tb_students--> <!-- </select>--> <!--resultMap标签用于定义实体类和数据表的映射关系(ORM)--> <resultMap id="studentMap" type="com.MyBatisDemo.pojo.Student"> <id column="sid" property="stuId"/> <result column="stu_num" property="stuNum"/> <result column="stu_name" property="stuName"/> <result column="stu_gender" property="stuGender"/> <result column="stu_age" property="stuAge"/> </resultMap> <!--resultMap 用于引用一个实体的映射关系,当配置了resultMap以后,resultType就可以省略--> <select id="listStudents" resultMap="studentMap"> select sid, stu_num, stu_name, stu_gender, stu_age from tb_students </select>
@Test public void testStudentList() { try { InputStream is = Resources.getResourceAsStream("mybatis-config.xml"); SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); SqlSessionFactory sqlSessionFactory = builder.build(is); SqlSession sqlSession = sqlSessionFactory.openSession(); StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class); List<Student> studentList = studentDAO.listStudents(); assertNotNull(studentList); sqlSession.commit(); for (Student student : studentList ) { System.out.println(student); } } catch (IOException e) { } }
5.5 查询操作--查询一条记录
根据学号查询一个学生信息
在StudentDAO中定义接口方法
package com.MyBatisDemo.dao; import com.MyBatisDemo.pojo.Student; import java.util.List; public interface StudentDAO { public int insertStudent(Student student); public int deleteStudent(String stuNum); public int updateStudent(Student student); public List<Student> listStudents(); public Student queryStudent(String stuNum); }
在StudentDAOMapper.xml中配置StudentDAO接口的方法实现--SQL
<?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文件相当于DAO接口的实现类,namespace属性要指定实现DAO接口的全限定名(全限定名:包名加类名)--> <!-- copy reference--> <mapper namespace="com.MyBatisDemo.dao.StudentDAO"> <!--parameterType 这个参数在DAO中已经指定类型了,在这里可以省略--> <insert id="insertStudent" parameterType="com.MyBatisDemo.pojo.Student"> insert into tb_students(stu_num, stu_name, stu_gender, stu_age) values (#{stuNum}, #{stuName}, #{stuGender}, #{stuAge}) </insert> <delete id="deleteStudent"> delete from tb_students where stu_num = #{stuNum} </delete> <update id="updateStudent"> update tb_students set stu_name = #{stuName}, stu_gender = #{stuGender}, stu_age = #{stuAge} where stu_num = #{stuNum} </update> <!-- resultType执行查询结果,封装的对象的实体类--> <!-- resultSets指定当前操作返回的集合类型(可省略)--> <!-- resultType返回的类型--> <!-- <select id="listStudents" resultType="com.MyBatisDemo.pojo.Student" resultSets="java.util.List">--> <!-- select--> <!-- sid as stuId,--> <!-- stu_num as stuNum,--> <!-- stu_name as stuName,--> <!-- stu_gender as stuGender,--> <!-- stu_age as stuAge--> <!-- from tb_students--> <!-- </select>--> <!--resultMap标签用于定义实体类和数据表的映射关系(ORM)--> <resultMap id="studentMap" type="com.MyBatisDemo.pojo.Student"> <id column="sid" property="stuId"/> <result column="stu_num" property="stuNum"/> <result column="stu_name" property="stuName"/> <result column="stu_gender" property="stuGender"/> <result column="stu_age" property="stuAge"/> </resultMap> <!-- resultMap 用于引用一个实体的映射关系,当配置了resultMap以后,resultType就可以省略--> <select id="listStudents" resultMap="studentMap"> select sid, stu_num, stu_name, stu_gender, stu_age from tb_students </select> <select id="queryStudent" resultMap="studentMap"> select sid, stu_num, stu_name, stu_gender, stu_age from tb_students where stu_num = #{stuNum} </select> </mapper>
单元测试--查询不需要提交事物
@Test public void testStudent() { try { InputStream is = Resources.getResourceAsStream("mybatis-config.xml"); SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); SqlSessionFactory sqlSessionFactory = builder.build(is); SqlSession sqlSession = sqlSessionFactory.openSession(); StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class); Student student = studentDAO.queryStudent("10002"); assertNotNull(student); //查询不用提交事务 System.out.println(student); } catch (IOException e) { } }
5.6 查询操作--多参数查询
分页查询
- 参数start, pagesize
在StudentDAO中定义操作方法,如果方法有多个参数,使用@Param注解声明参数的别名
package com.MyBatisDemo.dao; import com.MyBatisDemo.pojo.Student; import org.apache.ibatis.annotations.Param; import java.util.HashMap; import java.util.List; public interface StudentDAO { /* * 在MyBatis进行操作: * 1:如果操作方法只有一个简单类型或者字符串类型的参数, * 在Mapper配置中可以直接通过#{key}获取,这个key的占位符号可以随便写 * 2:如果操作方法有一个对象类型的参数, * 在Mapper配置中可以直接通过#{attrName}获取对象的指定属性值(attrName必须是参数对象的属性) * 3:如果操作方法有一个Map类型的参数, * 在Mapper配置中可以直接通过#{key}获取key的指定value值 * 4:如果操作方法有多个参数,该如何处理呢? * 通过MyBatis自带的参数 arg0 arg1 .. 来获取相应的参数 * 也可以通过@Param("key") 给参数添加注解的方式 */ public int insertStudent(Student student); public int deleteStudent(String stuNum); public int updateStudent(Student student); public List<Student> listStudents(); public Student queryStudent(String stuNum); // public List<Student> listStudentsByPage(HashMap<String,Integer> map); // public List<Student> listStudentsByPage(int start,int pageSize); public List<Student> listStudentsByPage(@Param("start") int start ,@Param("pageSize") int pageSize); }
在StudentMapper.xml配置sql时,使用#{}获取到指定的参数
<?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文件相当于DAO接口的实现类,namespace属性要指定实现DAO接口的全限定名(全限定名:包名加类名)--> <!-- copy reference--> <mapper namespace="com.MyBatisDemo.dao.StudentDAO"> <!--parameterType 这个参数在DAO中已经指定类型了,在这里可以省略--> <insert id="insertStudent" parameterType="com.MyBatisDemo.pojo.Student"> insert into tb_students(stu_num, stu_name, stu_gender, stu_age) values (#{stuNum}, #{stuName}, #{stuGender}, #{stuAge}) </insert> <delete id="deleteStudent"> delete from tb_students where stu_num = #{stuNum} </delete> <update id="updateStudent"> update tb_students set stu_name = #{stuName}, stu_gender = #{stuGender}, stu_age = #{stuAge} where stu_num = #{stuNum} </update> <!-- resultType执行查询结果,封装的对象的实体类--> <!-- resultSets指定当前操作返回的集合类型(可省略)--> <!-- resultType返回的类型--> <!-- <select id="listStudents" resultType="com.liguoqing.pojo.Student" resultSets="java.util.List">--> <!-- select--> <!-- sid as stuId,--> <!-- stu_num as stuNum,--> <!-- stu_name as stuName,--> <!-- stu_gender as stuGender,--> <!-- stu_age as stuAge--> <!-- from tb_students--> <!-- </select>--> <!--resultMap标签用于定义实体类和数据表的映射关系(ORM)--> <resultMap id="studentMap" type="com.MyBatisDemo.pojo.Student"> <id column="sid" property="stuId"/> <result column="stu_num" property="stuNum"/> <result column="stu_name" property="stuName"/> <result column="stu_gender" property="stuGender"/> <result column="stu_age" property="stuAge"/> </resultMap> <!-- resultMap 用于引用一个实体的映射关系,当配置了resultMap以后,resultType就可以省略--> <select id="listStudents" resultMap="studentMap"> select sid, stu_num, stu_name, stu_gender, stu_age from tb_students </select> <select id="queryStudent" resultMap="studentMap"> select sid, stu_num, stu_name, stu_gender, stu_age from tb_students where stu_num = #{stuNum} </select> <!-- <select id="listStudentsByPage" resultMap="studentMap">--> <!-- select sid,--> <!-- stu_num,--> <!-- stu_name,--> <!-- stu_gender,--> <!-- stu_age--> <!-- from tb_students limit #{start}, #{pageSize}--> <!-- </select>--> <select id="listStudentsByPage" resultMap="studentMap"> select sid, stu_num, stu_name, stu_gender, stu_age from tb_students limit #{start}, #{pageSize} <!-- limit #{arg0}, #{arg1}--> <!-- limit #{param1}, #{param2}--> </select> </mapper>
注意:如果DAO操作方法,没有通过@Param指定参数别名,在SQL中也可以通过MyBatis自带的arg0,arg1...或者param1,param2...获取参数
单元测试:
@Test public void testlistStudentsByPage() { try { InputStream is = Resources.getResourceAsStream("mybatis-config.xml"); SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); SqlSessionFactory sqlSessionFactory = builder.build(is); SqlSession sqlSession = sqlSessionFactory.openSession(); StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class); // HashMap<String,Integer> map = new HashMap<String,Integer>(); // map.put("start",0); // map.put("pageSize",2); // List<Student> studentList = studentDAO.listStudentsByPage(map); List<Student> studentList = studentDAO.listStudentsByPage(0,2); assertNotNull(studentList); sqlSession.commit(); for (Student student : studentList ) { System.out.println(student); } } catch (IOException e) { } }
5.7 查询操作--查询总记录数
StudentDAO
package com.MyBatisDemo.dao; import com.MyBatisDemo.pojo.Student; import org.apache.ibatis.annotations.Param; import java.util.HashMap; import java.util.List; public interface StudentDAO { /* * 在MyBatis进行操作: * 1:如果操作方法只有一个简单类型或者字符串类型的参数, * 在Mapper配置中可以直接通过#{key}获取,这个key的占位符号可以随便写 * 2:如果操作方法有一个对象类型的参数, * 在Mapper配置中可以直接通过#{attrName}获取对象的指定属性值(attrName必须是参数对象的属性) * 3:如果操作方法有一个Map类型的参数, * 在Mapper配置中可以直接通过#{key}获取key的指定value值 * 4:如果操作方法有多个参数,该如何处理呢? * 通过MyBatis自带的参数 arg0 arg1 .. 来获取相应的参数 * 也可以通过@Param("key") 给参数添加注解的方式 */ public int insertStudent(Student student); public int deleteStudent(String stuNum); public int updateStudent(Student student); public List<Student> listStudents(); public Student queryStudent(String stuNum); // public List<Student> listStudentsByPage(HashMap<String,Integer> map); // public List<Student> listStudentsByPage(int start,int pageSize); public List<Student> listStudentsByPage(@Param("start") int start ,@Param("pageSize") int pageSize); public int getCount(); }
在StudentMapper.xml配置sql时,使用resultType指定当前操作的返回类型为int
<select id="getCount" resultType="int"> select count(1) from tb_students </select>
单元测试:
@Test public void testGetStudentCount(){ try{ InputStream is = Resources.getResourceAsStream("mybatis-config.xml"); SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); SqlSessionFactory sqlSessionFactory = builder.build(is); SqlSession sqlSession = sqlSessionFactory.openSession(); StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class); int i = studentDAO.getCount(); System.out.println(i); }catch (IOException e){ } }
5.8 添加操作回填生成的主键
在StudentMapper.xml的insert标签中,useGeneratedKeys设置添加操作是否需要回填生成的主键keyProperty设置回填的位置
<!--parameterType 这个参数在DAO中已经指定类型了,在这里可以省略--> <!--useGeneratedKeys 设置添加操作是否需要回填生成的主键--> <!--keyProperty 设置回填的位置--> <insert id="insertStudent" parameterType="com.MyBatisDemo.pojo.Student" useGeneratedKeys="true" keyProperty="stuId"> insert into tb_students(stu_num, stu_name, stu_gender, stu_age) values (#{stuNum}, #{stuName}, #{stuGender}, #{stuAge}) </insert>
0x06 MyBatis工具类封装
MyBatisUtil
package com.MyBatisDemo.utils; 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 java.io.IOException; public class MyBatisUtil { private static SqlSessionFactory factory;//单例的 private static final ThreadLocal<SqlSession> local = new ThreadLocal<SqlSession>(); //线程锁,各个线程之间不影响,例如开启关闭数据库连接之类的 static { try { //加载myBatis配置文件,创建会话工厂 factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml")); } catch (IOException e) { e.printStackTrace(); } } //得到sqlSession对象 public static SqlSession getSqlSession() throws IOException { SqlSession sqlSession = local.get(); if (sqlSession == null){ sqlSession = factory.openSession(); local.set(sqlSession); } return sqlSession; } public static <T extends Object>T getMapper(Class<T> C) throws IOException { SqlSession sqlSession = getSqlSession(); T dao = sqlSession.getMapper(C); return dao; } public static SqlSessionFactory getFactory() throws IOException { if (factory == null){ factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml")); } return factory; } }
增删改,需要commit操作
0x07 事务管理
sqlSession对象:
- getMapper(DAO.class)获取Mapper(DAO接口的实例)
- 事物管理
7.1 手动提交事物
sqlSession.commit() 提交事务
sqlSession.rollback() 事务回滚
@Test public void insertStudent() { try { // //加载mybatis配置文件 // InputStream is = Resources.getResourceAsStream("mybatis-config.xml"); // //创建builder // SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); // //会话工厂,连接工厂 // SqlSessionFactory factory = builder.build(is); // //sqlsession 代表数据库的连接,也代表数据库的连接对象 // //会话(连接) // SqlSession sqlSession = factory.openSession(); // StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class); // System.out.println(studentDAO); SqlSession sqlSession = MyBatisUtil.getSqlSession(); //1:当我们获取sqlSession对象时,就默认开启了事务 try{ StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class); Student student = new Student(0, "10005", "java少年", "男", 24); int i = studentDAO.insertStudent(student); //2:操作完成并成功以后,需要手动提交 sqlSession.commit(); System.out.println(student); System.out.println(i); }catch (Exception e){ //3:当操作出现异常,调用rollback进行回滚 sqlSession.rollback(); } } catch (IOException e) { e.printStackTrace(); } }
7.2 自动提交事务
sqlSession = factory.openSession(true);//这里的参数,如果不填,默认是false,也就是需要手动进行提交,如果填了true会自动提交事务
MyBatisUtil优化
package com.MyBatis.utils; 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 java.io.IOException; public class MyBatisUtil { private static SqlSessionFactory factory;//单例的 private static final ThreadLocal<SqlSession> local = new ThreadLocal<SqlSession>(); //线程锁,各个线程之间不影响,例如开启关闭数据库连接之类的 static { try { //加载myBatis配置文件,创建会话工厂 factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml")); } catch (IOException e) { e.printStackTrace(); } } //得到sqlSession对象 SqlSession sqlSession = local.get(); if (sqlSession == null){ sqlSession = factory.openSession(isAutoCommit);//这里的参数,如果不填,默认是false,也就是需要手动进行提交,如果填了true会自动提交事务 local.set(sqlSession); } return sqlSession; } public static <T extends Object>T getMapper(Class<T> C) throws IOException { SqlSession sqlSession = getSqlSession(true); T dao = sqlSession.getMapper(C); return dao; } public static SqlSessionFactory getFactory() throws IOException { if (factory == null){ factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml")); } return factory; } }
测试操作:
@Test public void testDeleteStudent() { try { // InputStream is = Resources.getResourceAsStream("mybatis-config.xml"); // SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); // //SqlSessionFactory表示mybatis的会话工厂,工厂模式 // SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is); // //SqlSession 对象是mybatis和数据库之间的连接,也就是会话,创建了连接,可以得到所有的mapper对象(映射关系) // SqlSession sqlSession = sqlSessionFactory.openSession(); // //通过SqlSession 对象调用getMapper方法获取DAO接口对象 // StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class); // //调用被测试方法 // int i = studentDAO.deleteStudent("10001"); // //提交事务 // sqlSession.commit(); // System.out.println(i); StudentDAO studentDAO = MyBatisUtil.getMapper(StudentDAO.class); int i = studentDAO.deleteStudent("10001"); } catch (IOException e) { e.printStackTrace(); } }
当有多个操作一般都使用手动提交
MyBatisUtil事务管理优化:
package com.MyBatisDemo.utils; 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 java.io.IOException; public class MyBatisUtil { private static SqlSessionFactory factory;//单例的 private static final ThreadLocal<SqlSession> local = new ThreadLocal<SqlSession>(); //线程锁,各个线程之间不影响,例如开启关闭数据库连接之类的 static { try { //加载myBatis配置文件,创建会话工厂 factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml")); } catch (IOException e) { e.printStackTrace(); } } //得到sqlSession对象 private static SqlSession getSqlSession(boolean isAutoCommit) throws IOException { SqlSession sqlSession = local.get(); if (sqlSession == null){ sqlSession = factory.openSession(isAutoCommit);//这里的参数,如果不填,默认是false,也就是需要手动进行提交,如果填了true会自动提交事务 local.set(sqlSession); } return sqlSession; } //手动事务管理 public static SqlSession getSqlSession() throws IOException { return getSqlSession(false); } //自动事务管理 public static <T extends Object>T getMapper(Class<T> C) throws IOException { SqlSession sqlSession = getSqlSession(true); T dao = sqlSession.getMapper(C); return dao; } public static SqlSessionFactory getFactory() throws IOException { if (factory == null){ factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml")); } return factory; } }
0x08 MyBatis主配置文件
mybatis-config.xml是MyBatis框架的主配置文件,主要用于配置MyBatis数据源以及工作属性信息
注意:
- 标签使用时需要注意顺序
- 空标签会导致报错
8.1 properties标签
- 可以定义键值对
- 可以引用属性文件
在resource文件夹下创建jdbc.properties文件,配置键值对如下:
mysql_driver=com.mysql.jdbc.Driver
mysql_url=jdbc:mysql://localhost:3306/jdbcpractice?characterEncoding = utf-8
mysql_username=root
mysql_password=123456
在mybatis-config.xml中通过properties标签引用jdbc.properties文件,引入后在配置environment时,可以直接使用jdbc.properties的key获取value
8.2 settings标签
<!--设置mybatis的属性--> <settings> <!--启动二级缓存--> <setting name="cacheEnable" value="true"/> <!--启用延迟加载--> <setting name="lazyLoadingEnabled" value="true"/> </settings>
8.3 typeAliases标签
<!--typeAliases用于给实体类取别名,在映射文件中可以直接使用别名来替代实体类的全限定名--> <typeAliases> <typeAlias type="com.liguoqing.pojo.Student" alias="Student"></typeAlias> </typeAliases>
8.4 plugins标签
<!--plugins主要用于配置MyBatis插件,例如分页插件--> <plugins> <plugin interceptor=""></plugin> </plugins>
8.5 environment
<!-- environments配置数据库连接信息 --> <!-- environments中可以定义多个environments标签(正式测试预发布),每个environments可以定义一套数据库连接配置--> <!-- default属性,用来执行使用哪个environment 标签,--> <environments default="mysql"> <environment id="mysql"> <!-- transactionManager用于配置数据库管理方法 type="JDBC" 可以进行事务的提交和回滚操作,type="MANAGED" 事务的提交和回滚由容器进行控制 --> <transactionManager type="JDBC"></transactionManager> <!-- dataSource标签用来配置数据库连接信息 POOLED|UNPOOLED 是否使用连接池--> <dataSource type="POOLED"> <property name="driver" value="${mysql_driver}"/> <property name="url" value="${mysql_url}"/> <property name="username" value="${mysql_username}"/> <property name="password" value="${mysql_password}"/> </dataSource> </environment> </environments>
8.6 mappers标签
<!-- mappers用于载入映射文件,载入DAO类--> <mappers> <mapper resource="mappers/StudentMapper.xml"></mapper> </mappers>
0x09 映射文件
9.1 MyBatis初始化
可以参考源码流程
9.2 Mappers根标签
mapper文件相当与DAO接口的实现类,namespace属性要执行实现DAO的全限定名(Copy Reference)
9.3 insert标签
声明添加操作(sql:insert...)
常用属性:
- id属性,绑定对应DAO中的方法
- parameterType属性,用以指定接口中对应方法的参数类型(可省略)
- useGeneratedKeys属性,用以设置添加操作是否需要回填生成的主键
- keyProperty属性,指定回填的id设置到参数对应中的哪个字段中
- timeout属性,设置超时时间,如果不设置会一直等待,如果设置了值并到时候还没执行,就会报错
主键回填:
方式一:
<insert id="insertStudent" parameterType="com.liguoqing.pojo.Student" useGeneratedKeys="true" keyProperty="stuId"> insert into tb_students(stu_num, stu_name, stu_gender, stu_age) values (#{stuNum}, #{stuName}, #{stuGender}, #{stuAge}) </insert>
方式二:
<insert id="insertStudent"> <selectKey keyProperty="stuId" resultType="java.lang.Integer"> select last_insert_id() </selectKey> insert into tb_students(stu_num, stu_name, stu_gender, stu_age) values (#{stuNum}, #{stuName}, #{stuGender}, #{stuAge}) </insert>
9.4 deleted标签
声明删除操作
9.5 update标签
声明修改操作
9.6 select标签
声明查询操作
- id属性,指定绑定方法的方法名
- resultType属性,指望从这条语句中返回结果的类全限定名或者别名;注意,如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身的类型。resultType和resultMap之间只能同时使用一个。
- resultMap属性,对外部resultMap的命名引用,结果映射是MyBatis最强大的特性,如果你对其理解透彻,许多复杂的映射问题都能迎刃而解。resultType和resultMap之间只能同时使用一个。
- parameterType属性,将会传入这条语句的参数的类全限定名或者别名。这个属性是可选的,因为MyBatis可以通过类型处理器(Typehandler)推断出具体传入语句的参数,默认值为未设置(unset)。
- useCache属性,指定查询操作是否使用缓存
- timeout属性,设置超时时间
- ...其他的属性可以查看MyBatis文档
9.7 resultMap标签
<!--resultMap标签用于定义实体类和数据表的映射关系(ORM)--> <resultMap id="studentMap" type="Student"> <id column="sid" property="stuId"/> <result column="stu_num" property="stuNum"/> <result column="stu_name" property="stuName"/> <result column="stu_gender" property="stuGender"/> <result column="stu_age" property="stuAge"/> </resultMap>
9.8 cache标签
设置当前dao进行数据库操作时的缓存属性设置
<cache type="" size="" readOnly="false"></cache>
9.9 sql和include
sql片段
<sql id="sqlpianduan">sid, stu_num, stu_name, stu_gender, stu_age</sql> <!-- resultMap 用于引用一个实体的映射关系,当配置了resultMap以后,resultType就可以省略--> <select id="listStudents" resultMap="studentMap"> select <include refid="sqlpianduan"/> from tb_students </select>
0x0A 分页插件
分页插件是一个独立于MyBatis之外的第三方插件;
10.1 添加分页插件的以来--PageHelper
<!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper --> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.2.0</version> </dependency>
10.2 配置插件
在MyBatis的主配置文件中mybatis-config.xml 中通过 plugins标签进行配置,注意标签的位置
<!-- plugins主要用于配置MyBatis插件,例如分页插件--> <plugins> <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin> </plugins>
10.3 分页实例
对学生信息进行分页查询
@Test public void testGetStudentByPage() throws IOException { StudentDAO studentDAO = MyBatisUtil.getMapper(StudentDAO.class);//sqlSession PageHelper.startPage(1,4);//分页,从第1页开始,往后的4条数据 List<Student> studentList = studentDAO.listStudents();//查全量就可以,分页组件会自动将数据进行分页 PageInfo<Student> pageInfo = new PageInfo<Student>(studentList); //返回的时候只需要将pageInfo 返回就可以,因为pageInfo中就包含了分页以及数据信信息 }
带条件的分页
package com.MyBatisDemo.dao; import com.MyBatisDemo.pojo.Student; import org.apache.ibatis.annotations.Param; import java.util.HashMap; import java.util.List; public interface StudentDAO { public List<Student> listStudentsByGender(String gender); } ———————————————————————————————————————————————————————————————————————————————— <select id="listStudentsByGender" resultMap="studentMap"> select <include refid="sqlpianduan"/> from tb_students where stu_gender = #{stuGender} </select> ————————————————————————————————————————————————————————————————————————————————— @Test public void testGetStudentByPageGender() throws IOException { StudentDAO studentDAO = MyBatisUtil.getMapper(StudentDAO.class); PageHelper.startPage(1,4); List<Student> girls = studentDAO.listStudentsByGender("女"); PageInfo<Student> pageInfo = new PageInfo<Student>(girls); //pageInfo中就包含了分页以及数据信息 for(Student s : pageInfo.getList()){ System.out.println(s); } }
0x0B 关联映射
11.1 实体关系
实体--数据实体,实体关系指的就是数据和数据之间的关系
例如:用户和角色,房屋和漏洞,订单和商品
实体关系分为以下四种:
11.1.1 一对一关联
实例:人和身份证,学生和学生证,用户基本信息和详情
数据表关系:
- 主键关连:用户表主键和详情表主键相同时,表示是匹配的数据
- 唯一外键关联
11.1.2 一对多,多对多关联
实例:
- 一对多:班级和学生,类别和商品
- 多对一:学生和班级,商品和类别
数据表关系:
- 在多的一端添加外键和一对一端进行关联
11.1.3 多对多关联
实例:用户和角色,角色和权限,房屋和业主,学生和社团,订单和商品
数据表关系:建立第三张关系表添加两个外键,分别与两张表的主键进行关联
用户--用户角色表--角色
11.2创建项目,部署MyBatis框架
11.2.1 创建web项目(Maven)
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> </web-app>
添加依赖
<dependencies> <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <!-- https://mvnrepository.com/artifact/javax.servlet/jsp-api --> <dependency> <groupId>javax.servlet</groupId> <artifactId>jsp-api</artifactId> <version>2.0</version> <scope>provided</scope> </dependency> </dependencies>
11.2.2 部署MyBatis框架
添加依赖
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.6</version> </dependency> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency>
11.2.3 配置文件
帮助类
package com.MyBatisDemo.utils; 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 java.io.IOException; public class MyBatisUtil { private static SqlSessionFactory factory;//单例的 private static final ThreadLocal<SqlSession> local = new ThreadLocal<SqlSession>(); //线程锁,各个线程之间不影响,例如开启关闭数据库连接之类的 static { try { //加载myBatis配置文件,创建会话工厂 factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml")); } catch (IOException e) { e.printStackTrace(); } } //得到sqlSession对象 private static SqlSession getSqlSession(boolean isAutoCommit) throws IOException { SqlSession sqlSession = local.get(); if (sqlSession == null){ sqlSession = factory.openSession(isAutoCommit);//这里的参数,如果不填,默认是false,也就是需要手动进行提交,如果填了true会自动提交事务 local.set(sqlSession); } return sqlSession; } //手动事务管理 public static SqlSession getSqlSession() throws IOException { return getSqlSession(false); } //自动事务管理 public static <T extends Object>T getMapper(Class<T> C) throws IOException { SqlSession sqlSession = getSqlSession(true); T dao = sqlSession.getMapper(C); return dao; } public static SqlSessionFactory getFactory() throws IOException { if (factory == null){ //加载主配置文件 factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml")); } return factory; } }
11.3 一对一关联
实例:用户和详情
11.3.1 创建数据表
#用户信息表 create table users( user_id int primary key auto_increment, user_name varchar(20) not null unique, user_pwd varchar(20) not null, user_realname varchar(20) not null, user_img varchar(100) not null ); #用户详情表 create table details( detail_id int primary key auto_increment, user_addr varchar(50) not null, user_tel char(11) not null, user_desc varchar(200), uid int not null unique );
11.3.2 创建实体类
- User
<!--这里的@注解是通过lombook包帮我们实现的--> <!--这里重写ToString 是为了打印--> @Data @NoArgsConstructor @AllArgsConstructor @ToString public class User { private int userId; private String userName; private String userPwd; private String userRealName; private String userImg; }
- Detail
@Data @NoArgsConstructor @AllArgsConstructor @ToString public class Detail { private int detailId; private String userAddr; private String userTel; private String userDesc; private int userId; }
11.3.3 添加操作
package com.MyBatisDemo.dao; import com.MyBatisDemo.pojo.User; public interface UserDao { public int insertUser(User user); }
测试代码:
@Test public void testInsertUser() throws IOException { //用户注册提交了基本信息和详情到Servlet,Servlet接收到注册信息,分装到User和Detail对象中 User user = new User(0,"zhangsan6","123123","张三","01.jpg",null); Detail detail = new Detail(0,"安徽合肥市","15866666666","heihei",0); SqlSession sqlSession = MyBatisUtil.getSqlSession(); try{ UserDao userDao = sqlSession.getMapper(UserDao.class); int i = userDao.insertUser(user);//添加用户 System.out.println(i); detail.setUserId(user.getUserId()); DetailDao detailDao = sqlSession.getMapper(DetailDao.class); int n = detailDao.insertDetail(detail);//添加详情 System.out.println(n); sqlSession.commit();//提交事务 }catch (Exception e){ e.printStackTrace(); sqlSession.rollback();//回滚事务 } }
11.3.4 一对一关联查询
在查询用户的同时关联查询出与之对应的详情
实体
映射文件
子查询
测试代码:
@Test public void testQueryUser() throws IOException { UserDao userDao = MyBatisUtil.getMapper(UserDao.class); User user = userDao.queryUser("zhangsan3"); System.out.println(user); }
11.4 一对多关联
案例:班级(一)--学生(多)
11.4.1 创建数据表
#创建班级信息表 CREATE TABLE classes( cid int PRIMARY key auto_increment, cname VARCHAR(30) not null unique, cdesc VARCHAR(100) ); #创建学生信息表 CREATE TABLE students( sid CHAR(5) PRIMARY key, sname VARCHAR(20) not null, sage int not null, scid int not null );
11.4.2 创建实体类
11.4.3 关联查询
当查询一个班级的时候,要关联查询出这个班级下的所有学生
连接查询的映射配置
子查询
注意mapper与主配置文件的关系以及变脸重命名之间的配置
子查询的映射配置
11.5 多对一关联
实例:学生(n)-- 班级(1)
当查询一个学生的时候,关联查询这个学生所在的班级信息
11.5.1 创建实体类
11.5.2 关联查询
连接查询的映射配置
子查询的映射配置
11.6 多对多查询
案例:学生(m)--课程(n)
11.6.1 创建数据表
#学生信息表如上 #课程信息表 CREATE TABLE courses( course_id int PRIMARY key auto_increment, course_name VARCHAR(50) not null ); #选课信息表/成绩表(学号,课程号,成绩) CREATE TABLE grades( sid CHAR(5) not null, cid int not null, score int not null );
11.6.2 关联查询
查询学生时,同时查询出学生选择的课程
根据课程编号查询课程时,同时查询选择了这门课程的学生
连接查询的映射配置
子查询的映射配置
0x0C 动态SQL
交友网,电商等网站都有筛选功能;
不同的需求筛选条件不同,需要动态的拼接sql;
用户的筛选条件不同,我们完成筛选执行的sql也不一样,我们可以通过穷举来完成不同条件的筛选,但是 这种实现思想过于繁琐和复杂,MyBatis就提供了动态SQL的配置方式来实现多条件的查询。
12.1 什么是动态SQL
根据搜索条件,动态完成SQL的拼接。
12.2 动态SQL使用案例
案例:心仪对象搜索
12.2.1 创建数据表
#数据表 CREATE TABLE members( member_id int PRIMARY key auto_increment, member_nick VARCHAR(20) not null UNIQUE, member_gender char(2) not null, member_age int not null, member_city varchar(30) not null );
12.2.2 创建实体类
@Data @NoArgsConstructor @AllArgsConstructor @ToString public class Member { private int memberId; private String memberNick; private String memberGender; private int memberAge; private String memberCity; }
12.2.3 创建DAO接口
在DAO接口中定义一个多条件查询的方法
public interface MemberDAO { //在多条件查询中,如果查询条件不确定,可以直接使用HashMap作为参数 public List<Member> searchMember(HashMap<String,Object> params); }
MemberMapper.xml
<resultMap id="memberMap" type="Member"> <id column="member_id" property="memberId"/> <result column="member_nick" property="memberNick"/> <result column="member_gender" property="memberGender"/> <result column="member_age" property="memberAge"/> <result column="member_city" property="memberCity"/> </resultMap> <select id="searchMember" resultMap="memberMap"> select member_id,member_nick,member_gender,member_age,member_city from members where 1 = 1 <if test="gender != null"><!--gender 就是参数对象的属性、参数Map的key--> and member_gender = #{gender} </if> <if test="minAge != null"><!--> 大于号 --> and member_age >= #{minAge} </if> <if test="maxAge != null"><!--< 小于号 --> and member_age <= #{maxAge} </if> <if test="city != null"> and member_city != #{city} </if> </select>
MemberDAOTest
@Test public void TestSearchMember() throws IOException { HashMap<String,Object> params = new HashMap<String,Object>(); params.put("gender","女"); // params.put("minAge",19); // params.put("maxAge",23); // params.put("city","武汉"); //当向Map中存放参数时,key必须与动态Sql中的参数保持一致 MemberDAO memberDAO = MyBatisUtil.getMapper(MemberDAO.class); List<Member> members = memberDAO.searchMember(params); System.out.println(members); }
12.2.4 where标签
12.2.5 trim标签
12.2.6 foreach标签
测试类:
@Test public void testSearchMemberByCity() throws IOException { List<String> cities = new ArrayList<String>(); cities.add("武汉"); cities.add("厦门"); MemberDAO memberDAO = MyBatisUtil.getMapper(MemberDAO.class); List<Member> members = memberDAO.searchMemberByCity(cities); for (Member m : members){ System.out.println(m); } }
0x0D 模糊查询
案例:根据昵称查询会员信息(模糊匹配like)
13.1 模糊查询实现
13.1.1 DAO
public interface MemberDAO { //根据昵称查询用户信息-- 模糊查询 //模糊查询需要使用 ${} 取值,与SQL进行拼接 //在使用${}时,即使只有一个参数也需要使用@Param注释声明参数的key (非String类型不需要) public List<Member> searchMemberByNick(@Param("keyWord") String keyWord); }
13.1.2 映射文件
<!--如果参数是String 类型,需要使用parameterType声明参数类型--> <select id="searchMemberByNick" parameterType="java.lang.String" resultMap="memberMap"> select member_id,member_nick,member_gender,member_age,member_city from members where member_nick like '%${keyWord}%' </select>
13.1.3 测试
@Test public void testSearchMemberByNick() throws IOException { MemberDAO memberDAO = MyBatisUtil.getMapper(MemberDAO.class); List<Member> members = memberDAO.searchMemberByNick("丽"); for (Member m : members){ System.out.println(m); } }
13.2 #{}和${}的区别
<!--${keyWord} 表示获取参数,先获取参数的值,拼接到SQL语句中,再编译执行SQL语句 可能引起SQL注入问题--> <!--#{keyWord} 表示获取参数,先完成SQL的编译(预编译),预编译之后再将获取的参数设置到SQL中 可以避免SQL注入问题-->
0x0E MyBatis日志配置
MyBatis作为一个封装好的ORM框架,其运行过程中我们没办法跟踪,为了让开发者了解MyBatis执行流程以及每个执行步骤所完成的工作,
MyBatis框架本身集成了log4j日志框架,对运行的过程进行跟踪记录,我们只需要对MyBatis进行相关的日志配置,就可以看到MyBatis运行过程中的日志信息。
14.1 添加日志框架依赖
<!-- https://mvnrepository.com/artifact/log4j/log4j --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
14.2 添加日志配置文件
- 在resources目录下创建名为log4j.properties文件,名称必须如此
- 在log4j.properties文件中配置日志输出方式
# 声明日志的输出级别及输出格式 log4j.rootLogger=DEBUG,stdout # MyBatis logging configuration... log4j.logger.org.mybatis.example.BlogMapper=TRACE # Console output ... log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout # 定义日志的打印格式 %t表示 线程名称 %5p 日志级别 %msg 日志信息 \:%m%n 换行 log4j.appender.stdout.layout.ConversionPattern=[%t] %5p -%msg \:%m%n
14.3 日志信息的等级
0x0F 配置数据库连接池--整合Druid
MyBatis作为一个ORM框架,在进行数据库操作的时候是需要和数据库连接的,MyBatis支持基于数据库连接池的连接创建方式。
当我们配置MyBatis数据源时,只要配置了dataSource标签的type属性值为pooled时,就可以使用MyBatis内置的连接池管理连接。
如果我们想要使用第三方的数据库连接池,则需要进行自定义配置。
15.1 常见的连接池
- DBCP
- C3P0
- Druid 性能比较好,提供了比较便捷的监控系统
- Hikari 性能最好
15.2 添加Druid依赖
<!-- https://mvnrepository.com/artifact/com.alibaba/druid --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.10</version> </dependency>
15.3 创建Druid连接池工厂
public class DruidDataSourceFactory extends PooledDataSourceFactory { public DruidDataSourceFactory() { this.dataSource = new DruidDataSource(); } }
15.4 将DruidDataSourceFactory配置给MyBatis数据源
<environments default="mysql"> <environment id="mysql"> <transactionManager type="jdbc"></transactionManager> <!--POOLED 使用MyBatis内置的连接池实现 --> <!--mybatis 需要一个连接池工厂 这个工厂可以产生数据库连接池 PooledDataSourceFactory --> <!--使用了多态--> <dataSource type="com.MyBatisDemo.utils.DruidDataSourceFactory"> <property name="driverClass" value="${driver}"/> <property name="jdbcUrl" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments>
0x10 MyBatis缓存
MyBatis是基于JDBC的封装,使数据库操作更加便捷,MyBatis除了对JDBC操作步骤进行封装外也对其性能进行了优化
- 在MyBatis引入了缓存机制,用于提升MyBatis的检索效率
- 在MyBatis引入了延迟加载机制,用于对数据库不必要的访问
16.1 缓存的工作原理
缓存,就是存储数据的内存
16.2 MyBatis缓存
MyBatis缓存分为一级缓存和二级缓存。
16.2.1 一级缓存
一级缓存也叫做SqlSession缓存,为每个sqlsession单独分配的缓存内存,无需手动开启,可直接使用。
多个sqlsession的缓存是不共享的。
特性:
1:如果多次查询使用的是同一个Sqlsession对象,则第一次查询以后,数据会存放到缓存,后续的查询则直接访问缓存中存储的数据。
2:如果第一次查询完成后,对查询出的对象进行修改(此修改会影响到缓存),第二次查询会直接访问缓存,造成第二次查询的结果与数据库不一致。
3:当我们进行再次查询时,想要跳过缓存直接查询数据库,则可以通过sqlsession.clearCache();来清除当前SqlSession的缓存。
4:如果第一次查询以后,第二次查询以前,使用当前的sqlsession执行了修改操作,此修改操作会使第一次查询并缓存的数据失效,因此第二次查询会再次访问数据库。
测试代码:
@Test public void testQueryMemberById() throws IOException { SqlSession sqlSession = MyBatisUtil.getFactory().openSession(); SqlSession sqlSession2 = MyBatisUtil.getFactory().openSession(); // SqlSession sqlSession = MyBatisUtil.getSqlSession(); // SqlSession sqlSession2 = MyBatisUtil.getSqlSession(); MemberDAO memberDAO = sqlSession.getMapper(MemberDAO.class); Member member1 = memberDAO.queryMemberById(1); System.out.println(member1); member1.setMemberAge(200); sqlSession.clearCache(); System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); MemberDAO memberDAO2 = sqlSession.getMapper(MemberDAO.class); Member member2 =memberDAO2.queryMemberById(1); System.out.println(member2); }
16.2.2 两次查询与数据库不一致问题
16.2.3 二级缓存
二级缓存也成为SqlSessionFactory级缓存。通过同一个factory对象获取的SqlSession可以共享二级缓存;
在应用服务器中SqlSessionFactory是单例的,因此我们二级缓存可以实现全局共享。
特性:
1:二级缓存默认没有开启,需要在mybatis-config.xml中的setting标签中开启
2:二级缓存只能缓存实现了序列化接口的对象
- 在mybatis-config.xml 开启使用二级缓存
<settings> <setting name="cacheEnabled" value="true"/> </settings>
- 在需要使用二级缓存的Mapper文件中配置cache标签使用二级缓存
<cache/> //该标签中有一些属性可以自行进行配置 //例如 淘汰策略属性: eviction = "FIFO" 先进先出 更新频率属性: flushInterval = "6000" 间隔多长时间刷新一次缓存 缓存区的大小属性:size = "223" 多少个对象的引用 只读属性: readOnly = "true" 只能读不能改
- 被缓存的实体类实现序列化接口
@Data @NoArgsConstructor @AllArgsConstructor @ToString public class Member implements Serializable { private int memberId; private String memberNick; private String memberGender; private Integer memberAge; private String memberCity; }
- 测试
@Test public void testQueryMemberById() throws IOException { SqlSessionFactory factory = MyBatisUtil.getFactory(); //1:多个sqlsession对象来自于同一个sqlsessionFactory SqlSession sqlSession = factory.openSession(true); SqlSession sqlSession2 = factory.openSession(true); MemberDAO memberDAO = sqlSession.getMapper(MemberDAO.class); Member member1 = memberDAO.queryMemberById(1); System.out.println(member1); // sqlSession.clearCache(); sqlSession.commit(); //2:第一次查询之后,执行sqlSession.commit() 会将当前sqlsession的查询结果缓存到二级缓存 System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); MemberDAO memberDAO2 = sqlSession2.getMapper(MemberDAO.class); Member member2 =memberDAO2.queryMemberById(1); System.out.println(member2); }
16.3 查询操作的缓存开关
<select id="queryMemberById" resultMap="memberMap" useCache="false"> select member_id,member_nick,member_gender,member_age,member_city from members where member_id=#{mid} </select>
0x11 延迟加载
- 开启延迟加载
fetchType="lazy"
<?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.guoqing.dao.ClassDAO"> <resultMap id="classMap" type="Clazz"> <id column="cid" property="classId"/> <result column="cname" property="className"/> <result column="cdesc" property="classDesc"/> <collection property="stus" select="com.guoqing.dao.StudentDAO.queryStudentsByCid" column="cid" fetchType="lazy"/> </resultMap> <select id="queryClassByCid" resultMap="classMap"> select cid,cname,cdesc from classes where cid = #{cid} </select> </mapper>
- 测试类
@Test public void queryClassByCid() throws IOException { ClassDAO classDAO = MyBatisUtil.getMapper(ClassDAO.class); Clazz clazz = classDAO.queryClassByCid(1); System.out.println(clazz.getClassName()); System.out.println(clazz.getStus()); }