文章目录
简介
mybatis框架封装了数据层(Dao),对应Java基础中的JDBC部分。将JDBC中重复的连接数据库获取数据的代码进行了整合,其中的变量(例如:数据库地址,账号,密码,想执行的sql等)通过xml文件配置,让开发专注在业务逻辑上,屏蔽底层大量冗余的代码。
流程简述
MyBatis 的 XML 配置文件(名字自取,本篇为mybatisConfig.xml)包含了全局的设置和mapper映射,通过读取xml配置文件,SqlSessionFactoryBuilder可以创建一个SqlSessionFactory,SqlSessionFactory用于创建SqlSession, 通过用 SqlSession 实例来执行从mapper.xml中映射的SQL 语句。大致流程如下:
最简单的调用demo:
public class BaseTest {
public static void main(String[] args) {
String resource = "mybatisConfig.xml";
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(resource);
//读取全局XML 映射配置文件创建工厂
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//生产SqlSession
SqlSession session=sqlSessionFactory.openSession();
//通过SqlSession执行mapper.xml中映射的sql
List<SysUser> list = session.selectList("com.mybatis.mapper.SysUserMapper.selAll");
for (SysUser user : list) {
System.out.println(user.toString());
}
//事务处理
//session.rollback();
//session.commit();
//释放资源
session.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
环境搭建
1,Ant项目:自行下载jar包,添加mybatis-x.x.x.jar
2,Maven项目:pom.xml中添加依赖
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.3.0</version>
</dependency>
开发配置
XML 全局配置文件——mybatisConfig.xml
作为mybais的全局配置,其中所有的标签都必须按照流程简述中的顺序写,否则会报错。
properties
定义属性,如下面这段代码中定义的属性,在整个全局配置xml文件中可以通过${username}
${password}
来引用
<properties resource="org/mybatis/example/config.properties">
<property name="username" value="dev_user"/>
<property name="password" value="F2Fa3!33TYyg"/>
</properties>
- | - |
---|---|
resource属性 | 引用的properties配置文件路径 |
property标签 | 定义的属性 |
若两者存在同名属性,resource引用的配置文件中的属性由于是后加载会覆盖property标签中属性的值
settings
有很多设置,后续用到会补充,目前暂时设置一个日志
<settings>
<!--mybatis日志启用配置-->
<setting name="logImpl" value="LOG4J"/>
</settings>
typeAliases
给数据库对应的实体类(pojo)定义一个别名,就不用全路径引用
<typeAliases>
<!--给某个类定义一个别名(区分大小写)-->
<typeAlias type="com.mybatis.pojo.SysUser" alias="user"/>
<!--定义一个包,则类可以直接通过类名(不区分大小写)引用-->
<package name="com.mybatis.pojo"/>
</typeAliases>
environments
配置数据库连接信息
<!--default引用environment的id,当前使用的环境 -->
<environments default="default">
<!--声明可使用的环境 -->
<environment id="default">
<!--使用原生JDBC事务 / MANAGED 事务管理转交给其他容器-->
<transactionManager type="JDBC"/>
<!--POOLED:使用数据库连接池(tomcat的context.xml文件需要配置)
UNPOOLED:不实用数据库连接池,和直接使用 JDBC 一样
JNDI:java 命名目录接口技术-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/zee"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
mappers
定义对应的mapper.xml文件,mapper.xml文件是mybatis的核心,定义了sql映射语句,此处可以使用相对于类路径的资源引用,或者字符表示,或 url 引用的完全限定名。
<mappers>
<!--配置某个xml文件映射-->
<mapper resource="com/mybatis/mapper/UserMapper.xml"/>
<!--配置一个包路径,包里所有的xml文件都自动注册映射-->
<package name="com.mybatis.mapper"/>
</mappers>
Mapper XML 文件
这里是整个mybatis的核心,日常开发中就是在此处编写sql,可以想象成一个DAO层的实现类。
<!-- namesapce:理解成dao层的实现类的全路径(包名+类名) -->
<mapper namespace="com.mybatis.mapper.SysUserMapper" >
</mapper>
sql标签
提取重复sql语句
<sql id="userColumns"> id,username,password </sql>
<select id="selectUsers" parameterType="int" resultType="hashmap">
select <include refid="userColumns"/>
from some_table
where id = #{id}
</select>
cache
缓存可以减少与数据库的交互,提高反应速度
- 一级缓存——同一个SqlSession执行同一个方法只会跟数据库交互一次,第一次之后把查询结果缓存到SqlSession缓存区(mybatis默认开启)
- 二级缓存——同一个SqlSessionFactory内的所有SqlSession共享一个缓存区
通过在mapper.xml文件中添加:
<cache readOnly="true"></cache>
- 自定义缓存——实现Cache接口,可以自定义缓存方式,可配置redis等NOSQL数据库
select ,insert, update,delete标签
对应sql语句,通过属性可以根据业务逻辑来定义sql
select ,insert, update,delete标签使用方法类似
<mapper namespace="com.mybatis.mapper.SysUserMapper" >
<select id="selById" resultType="user" parameterType="int">
select * from sys_user s where s.id=#{param1}
</select>
</mapper>
属性 | 作用 |
---|---|
id | 相当于实体类的方法名 |
resultType | 相当于方法的返回值,查询sql返回的是一个结果集,此处写其中一条结果对应的java类(pojo可以应用typeAliases定义的别名) |
parameterType | 相当于方法的参数 |
使用parameterType传参遵循以下规则:
基础数据类型/String | #{xxx}:使用parameterType传送一个基础数据类型参数随便填都可以正确传入参数 |
Map | #{key}:通过key来占位 |
List | #{list[0]}:通过#{list[index]}来用list中某一条数据占位 |
对象 | #{pror}:通过属性(自动调用属性的get方法)自动匹配占位 |
对于map和对象也可以通过
${key}
${pror}
来拼接sql,但是这种方式在底层是通过字符串直接拼接,存在sql注入风险
resultMap
从数据库的数据到javaBean的对象,使用resultType属性时,是利用了Auto-Mapping特性,底层是自动把sql查询出来的字段和resultType指定的类的属性对应,调用set方法封装,而如果程序员想要自己定义mapping,可以采用resultMap属性。
动态SQL
在编写上面的sql时,有时业务需求要根据传入参数细微调整sql,因此有多个标签帮助用于动态构建sql
- 判断
if
条件为true则在sql中拼接标签内的内容
<select id="selByIdByName" resultType="com.mybatis.pojo.Student">
select * from student where
<!--条件判断-->
<if test="id != null and id !=''">
and id = #{id}
</if>
<if test="name != null and name !=''">
and name = #{name}
</if>
</select>
choose, when, otherwise
类似于java中的switch,case;只会有一个条件满足被拼接在sql后
<select id="selByIdByName" resultType="com.mybatis.pojo.Student">
select * from student
<where>
<!--多个条件只成立一个-->
<choose>
<when test="id != null and id !=''">and id = #{id}</when>
<when test="name != null and name !=''">and name = #{name}</when>
<otherwise>and 1=1</otherwise>
</choose>
</where>
</select>
- 消除
对于上面的if中的例子,拼接出来的sql会报错,应为where后面直接接了and,mybatis提供了消除这种错误的方式
where
会自动去除where后面跟的and或者or等关键字
<select id="selByIdByName" resultType="com.mybatis.pojo.Student">
select * from student
<where>
<!--条件判断-->
<if test="id != null and id !=''">
and id = #{id}
</if>
<if test="name != null and name !=''">
and name = #{name}
</if>
</where>
</select>
set
在update语句中set会遇到跟where一样的多出来and
的一样的情况多出来,
<update id="updateById" parameterType="com.mybatis.pojo.Student">
update student
<!--自动拼接set并智能去除后面的,-->
<set>
<if test="name != null and name !=''">name = #{name},</if>
</set>
</update>
trim
如业务上有其他需求可以通过trim来实现自定义拼接和去除
属性 | 用途 |
---|---|
prefix | 前缀加 |
prefixOverrides | 前缀去除(通过| 管道来定义多个,空格是必须的) |
suffix | 后缀加 |
suffixOverrides | 后缀去除 |
例如where
标签就等价于
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>
set
标签等价于
<trim prefix="SET" suffixOverrides=",">
...
</trim>
- 循环——foreach
在parameterType传参的小节中,没有list这种参数,集合参数的方法已经被内置在底层中,比如list.size() lise.isEmpty() 等都可以直接调用,作为if的判断依据,
<select id="selInById" parameterType="list" resultType="Student">
select * from student
<where>
<if test="list !=null and list.size > 0">
id in
<foreach collection="list" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</if>
</where>
</select>
属性 | 用途 |
---|---|
collection | List对象默认用"list"代替作为键,数组对象有"array"代替作为键,若作为map中的一个键值对传入,则以map中的key作为键,如有注解则使用注解定义的name |
item | 集合中元素迭代时的别名 |
index | 在list和数组中,index是元素的序号,在map中,index是元素的key |
open | foreach代码的开始符号,一般是(和close=")"合用。常用在in(),values()时。该参数可选 |
separator | 元素之间的分隔符,例如在in()的时候,separator=","会自动在元素中间用“,“隔开 |
close | foreach代码的关闭符号,一般是)和open="("合用。常用在in(),values()时。该参数可选。 |
- bind
创建一个变量并绑定在ognl的Context中 value中的内容按XML格式解析,后面引用的id的值都变成bind之后的value
<select id="selByIdByName" resultType="com.mybatis.pojo.Student">
select * from student where id like
<!--bind标签:-->
<bind name="id" value="'%'+id"/>
#{id}<!---->
</select>
使用方式
直接调用
在最开始的流程简述时,我们使用的是一种通用的调用方式,通过SqlSession的方法去执行mapper.xml中的映射sql:
List<SysUser> list = SqlSession.selectList("com.mybatis.mapper.SysUserMapper.selAll");
接口绑定
使用接口绑定可以简化调用过程,避免重复的写mapper映射的类名,那接口绑定是什么东西呢?
前面我们把一个mapper.xml配置文件想象成一个实体类,如果给这个实体类定义一个接口,那么java多态的性质可以让我们使用接口来操作实体类(mapper.xml)里的方法。
具体操作起来很简单:
- 定义一个mapper.xml文件
<!--通过接口绑定则namespace要对应到接口-->
<mapper namespace="com.mybatis.mapper.StudentMapper" >
<select id="selAll" resultType="com.mybatis.pojo.Student">
select * from student
</select>
</mapper>
- 定义一个接口
接口的路径要与mapper.xml文件中的namespace一致
接口的方法名要于mapper.xml文件中的id一致
package com.mybatis.mapper;
import com.mybatis.pojo.Student;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
public interface StudentMapper {
List<Student> selAll();
}
- 调用
public class MapperBindInterfaceTest {
public static void main(String[] args) {
String resource = "mybatisConfig.xml";
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream("mybatisConfig.xml");
SqlSessionFactory sqlSessionFactory =
new SqlSessionFactoryBuilder().build(inputStream);
//生产SqlSession
SqlSession session=sqlSessionFactory.openSession();
//通过session获取实体类对应的mapper的接口
StudentMapper mapper = session.getMapper(StudentMapper.class);
//通过接口调用方法来操作mapper中定义的方法,执行sql
//原理:jdk的动态代理设计模式,创建了实现该接口的代理实现类
List<Student> students = mapper.selAll();
for(Student s:students){
System.out.println(s);
}
//释放资源
session.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
参数传递
通过前面得学习已经可以基本使用mybatis框架做简单的查询,但是使用parameterType只能传入单个参数,而在接口绑定部分我们将mapper.xml与接口联系起来,将id与接口方法联系起来,那么在接口中定义多个参数的时候,是可以直接传入多个参数的比如:
接口
package com.mybatis.mapper;
import com.mybatis.pojo.Student;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
public interface StudentMapper {
//不使用注解
List<Student> selInById(int id,String name,Map<String,Object> map);
//使用注解
//List<Student> selInById(@Param("id") int id,@Param("name") String name,@Param("stus") Map<String,Object> map);
}
mapper.xml文件:此时可以不用写parameterType
多参数的占位写法规则:
- 不使用注解:
只能使用#{0}
和#{param1}
这种顺序占位,若参数为Map,Javabean时,可以使用#{0.key}
或者#{param1.pror}
来获取参数
<?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要对应到接口-->
<mapper namespace="com.mybatis.mapper.StudentMapper" >
<select id="selInById" resultType="Student">
select * from student where id = #{0} and name = #{param2} and id = #{2.id}
</select>
</mapper>
- 使用注解
使用#{name}占位,name为接口定义的注解里面的name。若参数为Map,List,JavaBean,都可以按parameterType中同样的方式占位,如#{stus.id}
<?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要对应到接口-->
<mapper namespace="com.mybatis.mapper.StudentMapper" >
<select id="selInById" resultType="Student">
select * from student where id = #{id} and name = #{name} and id = #{stus.id}
</select>
</mapper>
调用
public class DynamicSqlTest {
public static void main(String[] args) {
String resource = "mybatisConfig.xml";
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//生产SqlSession
SqlSession session=sqlSessionFactory.openSession();
//通过session获取实体类对应的mapper的接口
StudentMapper mapper = session.getMapper(StudentMapper.class);
Map m = new HashMap();
m.put("id","1");
List<Student> students = mapper.selInById(1,"haoze",m);
System.out.println(students);
session.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
mybatis注解
在使用接口绑定的时候,我们可以使用注解来帮忙简化mapper.xml的配置。
在XML 映射配置文件——mybatisConfig.xml中配置mappers
<mappers>
<!-- 使用class属性则完全抛弃mapper.xml文件配置-->
<mapper class="com.mybatis.mapper.StudentMapper"/>
<!--使用package则注解和mapper.xml都可以起作用-->
<package name="com.mybatis.mapper"/>
</mappers>
在绑定的接口中使用注解。
package com.mybatis.mapper;
import com.mybatis.pojo.Student;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
import java.util.Map;
public interface StudentMapper {
@Select("select * from student")
List<Student> selAll();
}
若通过注解方式定义sql,那么mapper.xml中不要定义对应方法,否则会报错:
Error parsing SQL Mapper Configuration. Cause: java.lang.IllegalArgumentException: Mapped Statements collection already contains value for com.mybatis.mapper.StudentMapper.selAll