1、依赖
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.1</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.9</version> </dependency>
2、MyBatis主配置文件
<?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文件的位置,从类路径根开始找文件--> <properties resource="jdbc.properties"/> <!--设置日志级别--> <settings> <setting name="logImpl" value="STDOUT_LOGGING" /> </settings> <!--配置数据源--> <environments default="mydev"> <!--id:数据源的名称--> <environment id="mydev"> <!--配置事务类型:使用 JDBC 事务(使用 Connection 的提交和回滚) --> <transactionManager type="JDBC"/> <!--数据源 dataSource:创建数据库Connection对象 type: POOLED 使用数据库的连接池--> <dataSource type="POOLED"> <!--连接数据库的四个要素--> <property name="driver" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </dataSource> </environment> </environments> <!--指定mapper.xml文件位置,告诉mybatis要执行的sql语句的位置--> <mappers> <mapper resource="com/luca/dao/StudentDao.xml"/> </mappers> <mappers> <!--第一种方式:指定多个mapper文件--> <!--<mapper resource="com/luca/dao/StudentDao1.xml"/>--> <!--<mapper resource="com/luca/dao/StudentDao2.xml"/>--> <!--<mapper resource="com/luca/dao/StudentDao3.xml"/>--> <!--第二种方式:使用包名 name:xml文件(mapper文件),这个包中的所有xml文件一次都能加载给mybatis --> <package name="com.luca.dao"/> <!--定义别名--> <typeAliases> <!--起别名的第一种方式:可以指定一个类型的自定义别名 type:自定义类型的全限定名称 alias:别名(短小,容易记忆的) --> <!--<typeAlias type="com.luca.domain.Student" alias="stu"/>--> <!--<typeAlias type="com.luca.vo.ViewStudent" alias="vstu"/>--> <!--第二种方式 <package> name是包名,这个包中的所有类,类名就是别名(类名不区分大小写) 下面两个包中的所有类的类名,就是该类的别名 --> <package name="com.luca.domain"/> <package name="com.luca.vo"/> </typeAliases> </configuration>
根元素为<configuration>标签,主要包括:1)数据源;2) 指定mapper.xml文件位置;3)定义别名
(1) 配置数据源<dataSource>
-
<dataSource type="pooled" >的type实现 Mybatis 中连接池的配置。
-
UNPOOLED 不使用连接池的数据源, 实现了 javax.sq.DataSource 接口
-
POOLED 使用连接池的数据源, 实现了 javax.sq.DataSource 接口
-
JNDI 使用 JNDI 实现的数据源
-
定义连接数据库的四个要素。
-
事务:<transactionManager type="JDBC"/>指定mybatis使用JDBC的事务管理机制。
-
MyBatis支持两种事务管理器类型
-
JDBC:使用JDBC的事务管理机制。
-
MANAGED:由容器来管理事务的整个生命周期(如 Spring 容器)。
-
MyBatis将自动提交功能关闭了,改为了手动提交。设置自动提交:session = factory.openSession(true)
(2) 使用<properties resource="jdbc.properties"/>把数据库属性文件配置进来。
(3) <mappers>映射器
-
<mapper resource=""/> 使用相对于类路径的资源,从 classpath 路径查找文件,多个xxxMapper需要一个一个添加
-
<package name=""/> 指定包下的所有 Dao 接口,一次性把包内的所有xxxMapper加载进来
-
此种方法要求 Dao 接口名称和 mapper 映射文件名称相同,且在同一个目录中。(与Spring整合不需要此规则)
(4) 定义实体类别名的两种方式
-
<typeAlias type="com.luca.domain.Student" alias="stu"/> alias的值为别名
-
<package name="com.luca.domain"/> 类名即为别名
-
为啥用别名:ResultType值为全类名或别名,全类名太长,用别名更方便。
3、编写DAO接口
public interface StudentDao { //插入一条学生数据 int insertStudent(Student student); //查询所有数据 List<Student> selectStudents(); }
4、编写 Dao 接口 Mapper 的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"> <!-- namespace:必须有值,自定义的唯一字符串推荐使用: dao 接口的全限定名称 --> <mapper namespace="com.StudentDao"> <insert id="insertStudent"> insert into student values(#{id},#{name},#{email},#{age}) </insert> <!-- <select>: 查询数据, 标签中必须是 select 语句 id: sql 语句的自定义名称,推荐使用 dao 接口中方法名称,使用名称表示要执行的 sql 语句 resultType: 查询语句的返回结果数据类型,使用全限定类名 --> <select id="selectStudents" resultType="com.luca.domain.Student"> select * from student order by id desc </select> </mapper>
5、朴素用法(核心原理)
InputStream is = Resources.getResourceAsStream("mybatis.xml"); //读取配置文件 SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); //获得SqlSessionFactoryBuilder对象为了获得SqlSessionFactory SqlSessionFactory factory = builder.build(is); //创建 SqlSessionFactory 对象,目的是获取 SqlSession SqlSession sqlSession = factory.openSession(); //获取 SqlSession,SqlSession 能执行 sql 语句 String sqlId = "com.luca.dao.StudentDao.insertStudent"; //sqlId是dao接口中方法的全类名,对应mapper文件中的同名sql语句 int res = sqlSession.insert(sqlId, new Student(1004, "赵六", "dhweu@qq.com", 48)); //执行insert方法 sqlSession.commit(); //默认不自动提交事务,需要手动提交
(1)Resources 类(org.apache.ibatis.io.Resources)
Resources 类,顾名思义就是资源,用于读取资源文件。其有很多方法通过加载并解析资源文件,返回不同类型的 IO 流对象。
(2)SqlSessionFactoryBuilder 类
SqlSessionFactory 的 创 建 , 需 要 使 用 SqlSessionFactoryBuilder 对 象 的 build() 方 法 。 由 于SqlSessionFactoryBuilder 对象在创建完工厂对象后,就完成了其历史使命,即可被销毁。所以,一般会将该 SqlSessionFactoryBuilder 对象创建为一个方法内的局部对象,方法结束,对象销毁。
(3)SqlSessionFactory 接口
SqlSessionFactory 接口对象是一个重量级对象(系统开销大的对象),是线程安全的, 所以一个应用只需要一个该对象即可。 创建 SqlSession 需要使用 SqlSessionFactory 接口的的 openSession()方法。
-
openSession(true):创建一个有自动提交功能的 SqlSession
-
openSession(false):创建一个非自动提交功能的 SqlSession,需手动提交
-
openSession():同 openSession(false)
(4) SqlSession 接口
SqlSession 接口对象用于执行持久化操作。一个 SqlSession 对应着一次数据库会话,一次会话以SqlSession 对象的创建开始,以 SqlSession 对象的关闭结束。SqlSession 接口对象是线程不安全的,所以每次数据库会话结束前,需要马上调用其 close()方法,将其关闭。再次需要会话,再次创建。 SqlSession 在方法内部创建,使用完毕后关闭。
可将此代码创建为一个util类,通过调用工具类来获取sqlSession
public class MyBatisUtils { private static SqlSessionFactory factory = null; //使用 静态块 创建一次 SqlSessionFactory static { String config = "mybatis.xml"; //需要和你的项目中的文件名一致 try { InputStream is = Resources.getResourceAsStream(config); factory = new SqlSessionFactoryBuilder().build(is); } catch (IOException e) { e.printStackTrace(); } } //获取SqlSession的方法 public static SqlSession getSqlSession() { SqlSession session = null; if (factory != null) { session = factory.openSession(); //非自动提交事务 } return session; } }
6、传统用法
创建dao接口的实现类,实现接口中的方法。在方法体内调用工具类获取sqlSession对象,调用sqlSession中的方法实现sql操作。
public List<Student> selectStudents() { SqlSession session = MyBatisUtil.getSqlSession(); List<Student> studentList = session.selectList("com.bjpowernode.dao.StudentDao.selectStudents"); session.close(); return studentList; }
分析
-
Dao 的实现类其实并没有干什么实质性的工作,它仅仅就是通过 SqlSession 的相关 API 定位到映射文件 mapper 中相应 id 的 SQL 语句,真正对 DB 进行操作的工作其实是由框架通过 mapper 中的 SQL 完成的。
-
MyBatis 框架就抛开了 Dao 的实现类,直接定位到映射文件 mapper 中的相应 SQL 语句,对DB 进行操作。
-
这种对 Dao 的实现方式称为 Mapper 的动态代理方式。Mapper 动态代理方式无需程序员实现 Dao 接口。接口是由 MyBatis 结合映射文件自动生成的动态代理实现的。
7、MyBatis 框架 Dao 代理(高级用法)
(1) 去掉 Dao 接口实现类
(2) getMapper() 获取代理对象
StudentDao studentDao = MyBatisUtil.getSqlSession().getMapper(StudentDao.class);
(3) 使用 Dao 代理对象方法执行 sql 语句
StudentDao studentDao = MyBatisUtil.getSqlSession().getMapper(StudentDao.class); int nums = studentDao.insertStudent(new Student(1004, "赵六", "dhweu@qq.com", 48));
(4) 原理
-
studentDao为org.apache.ibatis.binding.MapperProxy代理对象
-
MapperProxy类invoke方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
try {
return method.invoke(this, args);
} catch (Throwable var5) {
throw ExceptionUtil.unwrapThrowable(var5);
}
} else {
MapperMethod mapperMethod = this.cachedMapperMethod(method);
return mapperMethod.execute(this.sqlSession, args);
}
}
-
MapperProxy类execute方法
public Object execute(SqlSession sqlSession, Object[] args) {
Object param;
Object result;
if (SqlCommandType.INSERT == this.command.getType()) {
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
} else if (SqlCommandType.UPDATE == this.command.getType()) {
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
}....
}
(5) 顶级用法说明(具体使用见SSM整合,目前使用上面的方法即可)
此方法为MyBatis框架实现DAO代理,还需要自定义工具类获取sqlSession对象,并非顶级用法。顶级用法是Spring整合MyBatis,在Spring配置文件中配置SqlSessionFactoryBean对象、MapperScannerConfigurer对象。自动扫描xxxDao接口和xxxDao.xml的Mapper配置文件。
升级版:在尚筹网项目中,可以将dao接口与mapper文件分开放,在spring-mybatis配置文件中配置dao接口和mapper文件的位置。
<!--配置SqlSessionFactoryBean整合mybatis--> <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <!--配置mybatis全局配置文件--> <property name="configLocation" value="classpath:mybatis-config.xml"/> <!--指定Mapper.xml配置文件的位置--> <property name="mapperLocations" value="classpath:mybatis/mapper/*Mapper.xml"/> </bean> <!--注册Mapper扫描配置器--> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!--指定SqlSessionFactory对象的id--> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"/> <!--指定包名,包名是dao接口所在的包名 MapperScannerConfigurer会扫描这个包中的所有接口,把每个接口都执行一次getMapper()方法, 得到每个接口的dao对象。把创建好的dao对象放入到spring的容器中。 --> <property name="basePackage" value="com.atguigu.crowd.mapper"/> </bean>
8、参数传递
(0) parameterType
在mapper文件中使用。用来限定调用sql语句时传入的参数类型。(目前感觉没啥用,以后再研究研究)
(1) 一个简单参数
Dao 接口中方法的参数只有一个简单类型(java 基本类型和 String),占位符 #{ 任意字符 },和方法的参数名无关。
接口方法: Student selectById(int id); mapper 文件: <select id="selectById" resultType="com.bjpowernode.domain.Student"> select id,name,email,age from student where id=#{studentId} </select> #{studentId} , studentId 是自定义的变量名称,和方法参数名无关。
(2) 多个参数-使用@Param
当 Dao 接口方法多个参数,需要通过名称使用参数。 在方法形参前面加入@Param(“自定义参数名”),mapper文件使用#{自定义参数名}。
接口方法: List<Student> selectMultiParam(@Param("personName") String name,@Param("personAge") int age); mapper 文件: <select id="selectMultiParam" resultType="com.bjpowernode.domain.Student"> select id,name,email,age from student where name=#{personName} or age = #{personAge} </select>
(3) 多个参数-使用对象
-
使用 java对象传递参数,对象的属性值就是sql需要的参数值。 每一个属性就是一个参数。
-
语法格式:#{ property,javaType=java 中数据类型名,jdbcType=数据类型名称 },javaType, jdbcType 的类型 MyBatis 可以检测出来,一般不需要设置。
-
常用格式:#{ property }
创建保存参数值的对象 QueryParam public class QueryParam { private String queryName; private int queryAge; //实现属性的set,get 方法 } 接口方法: List<Student> selectMultiObject(QueryParam queryParam); mapper 文件: <select id="selectMultiObject" resultType="com.bjpowernode.domain.Student"> select id,name,email,age from student where name=#{queryName} or age=#{queryAge} </select> 或 <select id="selectMultiObject" resultType="com.bjpowernode.domain.Student"> select id,name,email,age from student where name=#{queryName,javaType=string,jdbcType=VARCHAR}or age =#{queryAge,javaType=int,jdbcType=INTEGER} </select>
(4) 多个参数-按位置
-
参数位置从0开始,引用参数语法 #{arg位置},第一个参数是#{arg0},第二个是#{arg1}。
-
注意:mybatis-3.3版本和之前的版本使用#{0},#{1}方式,从mybatis3.4开始使用#{arg0}方式。
接口方法: List<Student> selectByNameAndAge(String name,int age); mapper 文件 <select id="selectByNameAndAge" resultType="com.bjpowernode.domain.Student"> select id,name,email,age from student where name=#{arg0} or age =#{arg1} </select>
(5) 多个参数-使用 Map
Map集合可以存储多个值,使用Map向mapper文件一次传入多个参数。Map集合使用String的key,Object类型的值存储参数。mapper文件使用#{key}引用参数值。
Map<String,Object> data = new HashMap<String,Object>(); data.put(“myname”,”李力”); data.put(“myage”,20); 接口方法: List<Student> selectMultiMap(Map<String,Object> map); mapper 文件: <select id="selectMultiMap" resultType="com.bjpowernode.domain.Student"> select id,name,email,age from student where name=#{myname} or age =#{myage} </select>
(6) 占位符#和$
-
#:告诉 mybatis 使用实际的参数值代替。使用 PrepareStatement 对象执行 sql 语句, #{…}代替sql 语句的“?”。 这样做更安全,更迅速,通常也是首选做法,
-
$:字符串替换,告诉mybatis使用$包含的“字符串”替换所在位置。使用 Statement 把 sql 语句和${}的内容连接起来。主要用在替换表名,列名,不同列排序等操作
9、封装 MyBatis 输出结果
(1) resultType
执行sql得到ResultSet转换的数据类型,使用类型的全限定名称或别名(别名的设置上第二点“主配置文件说明”)。
-
返回对象类型时:mybatis框架将ResultSet转换为指定的对象类型,使用类的空参构造方法创建对象,使用setXXX给属性赋值。
-
复习:类没有显示定义构造方法,默认有空参构造方法。如果显示了定义带参构造方法,则没有空参构造函数了。
-
所以要实现转换,1)类要有空参构造函数,2)要实现setXXX方法。
-
-
返回集合类型时:ResultType应该设置为集合包含的类型,而不是集合本身。resultType和resultMap,不能同时使用。
-
返回Map类型时:sql 的查询结果作为 Map 的 key 和 value。推荐使用 Map<Object,Object>。
-
注意: Map 作为接口返回值, sql 语句的查询结果最多只能有一条记录。 大于一条记录是错误。
-
接口方法: Map<Object,Object> selectReturnMap(int id); mapper 文件: <select id="selectReturnMap" resultType="java.util.HashMap"> select name,email from student where id = #{studentId} </select>
(2) resultMap
resultMap 可以自定义 sql 的结果和 java 对象属性的映射关系。更灵活的把列值赋值给java对象的属性。常用在列名和 java对象属性名不一致的情况。
<!--定义resultMap id:自定义名称,表示定义的resultMap type:java类型的全限定名称--> <resultMap id="studentMap" type="com.luca.domain.Student"> <!--列名和java属性关系--> <!--主键类,使用id标签 column:列名 property:java类型的属性名--> <id column="id" property="id"/> <!--非主键,使用result标签 column:列名 property:java类型的属性名--> <result column="name" property="name"/> <result column="email" property="email"/> <result column="age" property="age"/> </resultMap> <!--resultMap: resultMap 标签中的 id 属性值--> <select id="selectAllStudents" resultMap="studentMap"> select id,name,email,age from student </select>
(3) 实体类属性名和列名不同的处理方式
-
使用列别名和<resultType>,在mapper文件的sql语句添加列别名,列别名为java对象属性名
<select id="selectUseFieldAlias" resultType="com.bjpowernode.domain.PrimaryStudent"> select id as stuId, name as stuName,age as stuAge from student where name=#{queryName} or age=#{queryAge} </select>
-
使用<resultMap>,见上
(4) 模糊 like
-
java 代码中提供要查询的 “%力%”
接口方法: List<Student> selectLikeFirst("%力%"); mapper 文件: <select id="selectLikeFirst" resultType="com.bjpowernode.domain.Student"> select id,name,email,age from studentwhere name like #{studentName} </select>
-
mapper 文件中使用 like name "%" #{xxx} "%"
<select id="selectLikeSecond" resultType="com.bjpowernode.domain.Student"> select id,name,email,age from studentwhere name like "%" #{studentName} "%" </select>
10、动态SQL
动态 SQL,通过 MyBatis 提供的各种标签对条件作出判断以实现动态拼接 SQL 语句。这里的条件判断使用的表达式为OGNL表达式。常用的动态SQL标签有<if>、<where>、<choose/>、<foreach>等。MyBatis的动态SQL语句,与JSTL中的语句非常相似。
动态SQL,主要用于解决查询条件不确定的情况:在程序运行期间,根据用户提交的查询条件进行查询。提交的查询条件不同,执行的SQL语句不同。若将每种可能的情况均逐一列出,对所有条件进行排列组合,将会出现大量的SQL语句。此时,可使用动态SQL来解决这样的问题
(1) <if>
<select id="selectStudentIf" resultType="com.bjpowernode.domain.Student"> select id,name,email,age from studentwhere 1=1 <if test="name != null and name !='' "> and name = #{name} </if> <if test="age > 0 "> and age > #{age} </if> </select>
(2) <where>
<if/>标签的中存在一个比较麻烦的地方:需要在 where 后手工添加 1=1 的子句。如果where 后的所有<if/>条件均为 false,而 where 后若又没有 1=1 子句,则 SQL 中就会只剩下一个空的 where,SQL出错。
使用<where/>标签, 在有查询条件时,可以自动添加上where子句;没有查询条件时,不会添加where子句。需要注意的是,第一个<if/>标签中的SQL片断,可以不包含and。不过,写上 and也不错,系统会将多出的and去掉。但其它<if/>中SQL片断的and,必须要求写上。否则SQL语句将拼接出错。
<select id="selectStudentWhere" resultType="com.bjpowernode.domain.Student"> select id,name,email,age from student <where> <if test="name != null and name !=''"> and name = #{name} </if> <if test="age > 0"> and age > #{age} </if> </where> </select>
(3) <foreach>
用于实现对于数组与集合的遍历。
-
collection表示要遍历的集合类型list,array等。
-
open、close、separator为对遍历内容的SQL拼接。
语法:
<foreach collection="集合类型" open="开始的字符" close="结束的字符"item="集合中的成员" separator="集合成员之间的分隔符"> #{item的值即"集合中的成员"} </foreach>
-
遍历 List<简单类型>
需求:查询学生 id 是 1002,1005,1006 接口方法: List<Student> selectStudentForList(List<Integer> idList); mapper 文件: <select id="selectStudentForList" resultType="com.bjpowernode.domain.Student"> select id,name,email,age from student <if test="list !=null and list.size > 0 "> where id in <foreach collection="list" open="(" close=")" item="stuid" separator=","> #{stuid} </foreach> </if> </select>
-
遍历 List<对象类型>
接口方法: List<Student> selectStudentForList2(List<Student> stuList); mapper 文件: <select id="selectStudentForList2" resultType="com.bjpowernode.domain.Student"> select id,name,email,age from student <if test="list !=null and list.size > 0 "> where id in <foreach collection="list" open="(" close=")" item="stuobject" separator=","> #{stuobject.id} </foreach> </if> </select>
(4) 代码片段
<sql/>标签用于定义 SQL 片断,以便其它 SQL 标签复用。而其它标签使用该 SQL 片断,需要使用<include/>子标签。该<sql/>标签可以定义 SQL 语句中的任何部分,所以<include/>子标签可以放在动态SQL的任何位置。
<!--创建 sql 片段 id:片段的自定义名称--> <sql id="studentSql"> select id,name,email,age from student </sql> <select id="selectStudentSqlFragment" resultType="com.bjpowernode.domain.Student"> <!-- 引用 sql 片段 --> <include refid="studentSql"/> <if test="list !=null and list.size > 0 "> where id in <foreach collection="list" open="(" close=")" item="stuobject" separator=","> #{stuobject.id} </foreach> </if> </select>
11、PageHelper插件
(1) 依赖与插件
<dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.1.10</version> </dependency> <plugins> <plugin interceptor="com.github.pagehelper.PageInterceptor" /> #在<environments>之前加入 </plugins>
(2) PageHelper 对象
在你需要进行分页的 MyBatis 查询方法前调用PageHelper.startPage静态方法即可,紧跟在这个方法后的第一个 MyBatis 查询方法会被进行分页。
public void testSelect() throws IOException { //获取第 1 页, 3 条内容 PageHelper.startPage(1,3); List<Student> studentList = studentDao.selectStudents(); studentList.forEach( stu -> System.out.println(stu)); }
12、MyBatis总结思维导图