Mybatis
配置环境
mean配置
<!--数据库jar-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
<!--导入mybatis 这个下面内联jdbc jar-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.2</version>
</dependency>
<!--junit 单元测试jar 单独运行某一块-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>provided</scope>
</dependency>
mybatis.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>
<!--mybatis 核心配置文件中,标签有顺序properties?,settings?,typeAliases?,typeHandlers?,objectFactory?
,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?-->
<!--导入属性文件-->
<properties resource="config.properties"></properties>
<!--mybatis全局设置-->
<settings>
<!--启动mybatis日志功能-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
<!--是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!--设置类型别名 不写alias="user 默认类型是类名,类型别名不区分大小写 不太用这个-->
<typeAliases>
<!--<typeAlias type="com.pojo.User" alias="user"></typeAlias>-->
<!--以包为单位,将包下所有类型设置为默认的类型别名,即类名且不区分大小写-->
<package name="com.pojo"/>
</typeAliases>
<!-- environments 配置多个连接数据库的环境,数据库连接信息
属性:
default:设置默认使用环境的id
-->
<environments default="development"><!--用id来进行切换-->
<!--
environment 配置某个具体环境
属性
id 表示连接数据库环境的唯一标识,不能重复
-->
<environment id="development">
<!--transactionManager 事务管理方式
属性:type;"JDBC/MANAGED"
JDBC:表示当前环境中,执行SQL时,使用的时JDBC中原生的事务管理方式,事务的提交或回滚需要手动处理
MANAGED:表示被管理 例如spring
-->
<transactionManager type="JDBC"/>
<!--
dataSource配置数据源
属性:
type:设置数据源类型
type:"POOLED/UNPOOLED/JNDI"
POOLED 表示使用数据库连接池缓存数据库连接
UNPOOLED 表示不使用数据库连接池
JNDI 表示使用上下文中的数据源
-->
<dataSource type="POOLED"><!--数据源的管理-->
<property name="driver" value="${driverClassName}"/>
<property name="url"
value="${url}"/>
<property name="username" value="${uname}"/>
<property name="password" value="${pd}"/>
</dataSource>
</environment>
</environments>
<!--mappers 配置mybatis 引入sql映射文件-->
<mappers>
<mapper resource="mappers/UserMapper.xml"/>
<mapper resource="mappers/SelectMapper.xml"/>
<mapper resource="mappers/sqlMapper.xml"/>
<mapper resource="mappers/EmpMapper.xml"/>
<mapper resource="mappers/DeptMapper.xml"/>
<!--解决映射文件过多的方法
以包为单位引入映射文件
要求:1.mapper接口所在的包要和映射文件所在的包保持一致
2.mapper接口要和映射文件的名字保持一致
-->
</mappers>
</configuration>
config.properties连接数据库的文件
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/sgumybatis?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
uname=root
pd=123456
下来就是接口类 实体类 mapper映射文件
mybatis面向接口编程的两个一致 1.映射文件的namespace要和mapper接口的全类名保持一致 2.映射文件中的sql语句的id要和mapper接口中的方法名保持一致 表-实体类-mapper接口-映射文件
工具类
package com.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;
import java.io.InputStream;
public class JdbcUtil {
public static SqlSession getSqlSession() {
SqlSession sqlSession=null;
try {
//此处对异常进行try catch 否则在调用他的时候还要进行抛出处理
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory build = sqlSessionFactoryBuilder.build(resourceAsStream);
sqlSession = build.openSession(true);
} catch (IOException e) {
e.printStackTrace();
}
return sqlSession;
}
}
原生测试类
@Test
public void test() throws IOException {
/*
sqlsession默认手动提交事务,如需自动提交事务
将sqlSessionFactory.openSession(true) 设置为true自动自交
* */
//加载核心配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis.xml");
//获取sqlSessionFactoryBuilder 提供sqlsession工厂对象的一个构建对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//获取sqlSessionFactory
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(resourceAsStream);
//获取mybatis操作数据库的一个会话对象,sqlsession对象
//sqlSession代表数据库和Java程序之间的会话
SqlSession sqlSession = sqlSessionFactory.openSession(true);//默认是手动提交 设置为自动提交
//获取mapper接口对象 此方法传进去一个接口获得一个接口的实现类并且返回当前实现类的一个对象
//底层是代理模式
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//测试功能
int i = mapper.insertUser();
//事务提交
//sqlSession.commit();
System.out.println(i);
}
测试类
@Test
public void getAllUserTOMap(){
SqlSession sqlSession = JdbcUtil.getSqlSession();
SelectMapper selectMapper = sqlSession.getMapper(SelectMapper.class);
System.out.println(selectMapper.getAllUserTOMap());
}
mybatis获取值的两种方式
<?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.mapper.UserMapper"><!--namespace 命名空间-->
<insert id="insertUser">
insert into user value (null ,'admin','123456',23,'男','1456654@')
</insert>
<update id="updateUser">
update user set username='张三' where id=1
</update>
<delete id="deleteUser">
delete from user where id=2
</delete>
<!--resultType结果类型 设置默认的映射关系 resultMap结果映射 设置自定义的映射关系-->
<select id="getUserById" resultType="com.pojo.User">
select * from user where id=1
</select>
<select id="getAllUser" resultType="User">
select * from user
</select>
<!--mybatis获取值的两种方式 ${}和#{}
${} 本质是字符串拼接 会有sql注入 遇到字符串还要手动加单引号
#{} 本质是占位符赋值,尽量用这个
1.mapper接口方法参数为单个的字面量类型
可以通过 ${}#{} 来获取,${}要注意单引号问题
2.mapper接口方法中参数为多个时
mybatis会将参数放进map集合中,以两种方式进行储存
第一种以arg0,arg2为键,以参数为值
第二种以param1,param2为键,以参数为值
因此只需要以访问键的方式来访问值即可
3.mapper接口方法中参数为多个时,可以手动将多个参数放进map集合中
因此只需要以${}#{}访问键的方式来访问值即可
4.mapper接口方法的参数是一个实体类类型的参数时
只需要以${}和#{}以属性的方式访问属性值即可,但是${}要注意单引号问题
5.可以同过注解来命名参数@param
自定义将多条参数放进map集合中的键,一种以自己定义的@Param("username")的值为键,以参数为值
第二种以param1,param2为键,以参数为值
因此只需要以访问键的方式来访问值即可
-->
<select id="GetUserByUsername" resultType="com.pojo.User">
<!-- select * from user where username = '${username}' -->
select * from user where username =#{username}
</select>
<select id="chackLogin" resultType="com.pojo.User">
select * from user where username =#{arg0} and password=#{arg1}
</select>
<select id="checkLonginByMap" resultType="com.pojo.User">
select * from user where username =#{username} and password=#{password}
</select>
<insert id="addUser">
insert into user value (null ,#{username},#{password},#{age},#{sex},#{email})
</insert>
<select id="cheakLoginByParam" resultType="com.pojo.User">
select * from user where username =#{username} and password=#{password}
</select>
</mapper>
批量删除和表单查询 插入数据并获取主键
<?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.mapper.SqlMapper"> <!--namespace 命名空间--> 模糊查询 <select id="getUserByLike" resultType="com.pojo.User"> <!-- select * from user where username like '%${username}%'--> <!-- select * from user where username like concat('%',#{username},'%')--> select * from user where username like "%"#{username}"%" <!--建议使用这一种--> </select> <!--批量删除只能用$符号 因为批量删除不能带单引号,而#符号会自动添加单引号--> <delete id="deleteMor"> delete from user where id in (${ids}) </delete> <!--表名不能加单引号,只能用$来接收--> <select id="getUserByTableName" resultType="com.pojo.User"> select * from ${tableName} </select> <!--查到主键放到user对象中的id属性 useGeneratedKeys这个只在insert语句中有效,正常情况下useGeneratedKeys默认为false。 当useGeneratedKeys为true时,如果插入的表id以自增列为主键时,将会把该自增id返回。 keyProperty:将自增的主键的值赋值给传输文件中参数的某个属性中 --> <insert id="insertUser" useGeneratedKeys="true" keyProperty="id"> insert into user value (null ,#{username},#{password},#{age},#{sex},#{email}) </insert> </mapper>
mybatis的各种查询功能
<?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.mapper.SelectMapper"><!--namespace 命名空间-->
<!--
mybatis的各种查询功能
1.若查询出的 数据只有一条,可以通过实体类进行接收,或者list集合来接收
2.若查询出的数据有多条,一定不能通过实体类来接收,此时会抛出异常 TooManyResultsException,
可以用list集合来接收
也可以通过map集合接收
@Param("id") 结果:{password=123456, sex=男, id=1, age=23, email=1456654@, username=张三}
@MapKey("id")结果:{4={password=123, sex=男, id=4, age=23, email=16749786, username=李四}}
也可以在mapper接口的方法上添加@MapKey()注解,此时就可以将每条数据转换为map集合作为值,以某个字段的值作为键
然后同时放在一个map集合中
-->
<select id="getUserById" resultType="User">
select * from user where id=#{id}
</select>
<select id="getAllUser" resultType="com.pojo.User">
select * from user
</select>
<select id="getUserByIdToMap" resultType="java.util.Map">
select * from user where id=#{id}
</select>
<select id="getAllUserTOMap" resultType="java.util.Map">
select * from user
</select>
<!--
mybatis种设置了默认的类型别名
ava.lang.Integer int integer
int _int,_integer
Map map
String string
List list
-->
<select id="getCount" resultType="java.lang.Integer">
select count(*) from user
</select>
</mapper>
多对一查询
<?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.mapper.EmpMapper">
<!--namespace 命名空间-->
<!--设置别名,如果满足驼峰,开启全局配置驼峰命名规则emp_name映射为empName,就会将数据库_去掉映射-->
<select id="getAllEmpold" resultType="com.pojo.Emp">
select * from t_emp
</select>
<!--通过resultmap自定义映射关系
type处理查询数据的类型,映射关系中实体类类型
resultMap="resultMapEmp" 专门来处理某一个类型与我们查询结果之间的关系的,写的时某一个resultMap的id
id 设置主键映射关系
result 设置普通字段映射关系
property 设置映射关系中的属性名,必须时type属性所设置实体类类型中的属性名
column字段名 sql查询出来的字段名
-->
<resultMap id="resultMapEmp" type="Emp">
<id property="eid" column="eid"></id>
<result property="empName" column="emp_name"></result>
<result property="email" column="email"></result>
<result property="sex" column="sex"></result>
<result property="age" column="age"></result>
</resultMap>
<select id="getAllEmp" resultMap="resultMapEmp">
select * from t_emp
</select>
<!--
处理多对一的映射关系 1.级联属性赋值
-->
<resultMap id="empAndDeptResultMapOne" type="Emp">
<id property="eid" column="eid"></id>
<result property="empName" column="emp_name"></result>
<result property="email" column="email"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
<result property="dept.did" column="did"></result>
<result property="dept.deptName" column="dete_name"></result>
</resultMap>
<!--第二种方法 association处理多对一关系
property需要处理多对的映射关系的属性名
javaType 该属性的类型
-->
<resultMap id="empAndDeptResultMapTwo" type="Emp">
<id property="eid" column="eid"></id>
<result property="empName" column="emp_name"></result>
<result property="email" column="email"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
<association property="dept" javaType="Dept">
<result property="did" column="id"></result>
<result property="deptName" column="dete_name"></result>
</association>
</resultMap>
<select id="getEmpAndDept" resultMap="empAndDeptResultMapTwo">
select * from t_emp left join t_dept on t_emp.did=t_dept.did where t_emp.eid=#{eid}
</select>
</mapper>
分布查询
<!--分布查询-->
<resultMap id="gradeMap1" type="Grade">
<id column="id" property="id"></id>
<result column="name" property="name"></result>
<!--
select:设置分布式查询的sql的唯一标识(namespace,SQL id 或者mapper接口全类名.方法名)
column:设置分布查询条件
property 需要处理的实体类中的属性
-->
<collection property="students" javaType="List" ofType="Student" select="findStudentsByGradeId" column="id">
</collection>
</resultMap>
<select id="findGradeById1" parameterType="int" resultMap="gradeMap1">
select id, name
from gread
where id = #{id}
</select>
<select id="findStudentsByGradeId" parameterType="int" resultType="Student">
select name
from student
where gradeid = #{id}
</select>
一对多查询
<?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.mapper.DeptMapper">
<!--namespace 命名空间-->
<!--一对多 用集合 多对一 用对象
方式一
collection:处理一对多映射关系
ofType:标识该属性中所对应的集合中存储的数据类型
-->
<resultMap id="getDeptAndEmpResultMap" type="Dept">
<id property="did" column="did"></id>
<result property="deptName" column="dept_name"></result>
<collection property="emps" ofType="Emp">
<id property="eid" column="eid"></id>
<result property="empName" column="emp_name"></result>
<result property="age" column="age"></result>
<result property="email" column="email"></result>
<result property="sex" column="sex"></result>
</collection>
</resultMap>
<select id="getDeptAndEmp" resultMap="getDeptAndEmpResultMap">
select * from t_dept left join t_emp on t_dept.did = t_emp.did where t_dept.did=#{did}
</select>
</mapper>
动态sql
拼接sql语句 解决sql语句拼接时的痛点问题
-
if 标签
<!-- 动态sql:
1.if根据标签中test属性所对应的表达式巨顶标签中的内容是否需要执行到sql语句中
-->
<select id="getEmapByCondition" resultType="Emp">
select * from t_emp where 1=1
<if test="empName!=null and empName!=''">
and emp_name=#{empName}
</if>
<if test="age!=null and age!=''">
and age=#{age}
</if>
<if test="sex!=null and sex!=''">
and sex=#{sex}
</if>
<if test="email!=null and email!=''">
and email=#{email}
</if>
</select>
-
where标签
<!-- 动态sql: 2.where 标签 1.当where标签中有内容时,会自动生成where关键字,并且将内容前多余的and或者or去掉, 2.当where标签中没有内容时,此时where标签没有任何效果 注意:where标签不能将其中内容后面多余的and或or去掉 --> <select id="getEmapByCondition" resultType="Emp"> select * from t_emp <where> <if test="empName!=null and empName!=''"> and emp_name=#{empName} </if> <if test="age!=null and age!=''"> and age=#{age} </if> <if test="sex!=null and sex!=''"> and sex=#{sex} </if> <if test="email!=null and email!=''"> and email=#{email} </if> </where> </select>
-
trim标签
<!-- trim标签 prefix/prefixOverrides:将trim标签中内容前面或者后面添加指定的内容 prefixOverrides:/suffixOverrides:将trim标签中内容前面或者后面去掉指定的内容 trim 标签中无内容 trim标签无效果 --> <select id="getEmapByCondition3" resultType="Emp"> select * from t_emp <trim prefix="where" suffixOverrides="and|or"> <if test="empName!=null and empName!=''"> emp_name=#{empName} and </if> <if test="age!=null and age!=''"> age=#{age} or </if> <if test="sex!=null and sex!=''"> sex=#{sex} and </if> <if test="email!=null and email!=''"> email=#{email} </if> </trim> </select>
-
choose,when、otherwiser,相当于if..else if..else
<!-- choose when otherwiser 相当于if..else if..else when 至少要有一个 otherwise最多只能有一个 --> <select id="getEmapByCondition4" resultType="Emp"> select * from t_emp <where> <choose> <when test="empName!=null and empName!=''"> emp_name=#{empName} </when> <when test="age!=null and age!=''"> age=#{age} </when> <when test="sex!=null and sex!=''"> sex=#{sex} </when> <when test="email!=null and email!=''"> email=#{email} </when> <otherwise> did=1 </otherwise> </choose> </where> </select>
5.foreach标签
<!-- choose when otherwiser 相当于if..else if..else when 至少要有一个 otherwise最多只能有一个 --> <select id="getEmapByCondition4" resultType="Emp"> select * from t_emp <where> <choose> <when test="empName!=null and empName!=''"> emp_name=#{empName} </when> <when test="age!=null and age!=''"> age=#{age} </when> <when test="sex!=null and sex!=''"> sex=#{sex} </when> <when test="email!=null and email!=''"> email=#{email} </when> <otherwise> did=1 </otherwise> </choose> </where> </select>
-
sql标签
设置sql片段
引用sql片段
mybatis缓存 (面试会问)
一级缓存
是默认开启的,sqlssion级别的,通过同一个sqlsession查询的 数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会从数据库从新访问
一级缓存失效的四种情况:
不同的sqlsession对应不同的一级缓存
同一个sqlsession的是查询的条件不相同
同一个sqlsession两次查询期间执行了任何一次增删改操作
同一个sqlsession两次查询期间手动清理了缓存、
sqlSession.clearCache();//清空缓存
二级缓存
是SqlSessionFactory级别的,通过同一个SqlSessionFactory创建的SqlSession查询的结果会被缓存,此后若再次执行相同查询语句,结果就会从缓存中获取
二级缓存开启的条件:
在核心配置文件中设置全局配置属性 cacheEndabled="true",默认为true不需要设置
在映射xml文件中设置标签,<cache></cache>
二级缓存必须要在SqlSession关闭或者提交之后才有效
查询的数据所转换的实体类类型必须实现序列化的接口 public class Emp implements Serializable {
二级缓存失效情况:
两次查询之间进行了任意的增删改,会使一级缓存和二级缓存同时失效
mybatis缓存查询的顺序
先查询二级缓存,因为二级缓存中可能会有其他程序已经查询出来的数据,可以拿来直接使用
如果二级缓存中没有,再查询一级缓存
如果一级缓存中也没有,再查询数据库
sqlsession关闭后,一级缓存中的数据会写入二级缓存