mybatis框架
1、mybatis框架介绍
MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。
iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBATIS提供的持久层框
架包括SQL Maps和Data Access Objects(DAO)。
MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架。MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索封装。MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录。
更加简化jdbc代码,简化持久层,sql语句从代码中分离,利用反射,将表中数据与java bean 属性一一
映射 即 ORM(Object Relational Mapping 对象关系映射)。
由于mybatis底层运用反射,所以实体类要添加空构造器实例化,否则会报错!
2、框架环境搭建
1、导入jar包及其mybatis依赖jar包(如:Ojdbc6.jar、log4j.jar、log4j-api.jar)
2、在src目录下编写mybatis-config.xml配置文件,设置连接数据库的url、username、password、driver。
[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 default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<!--resource下设置mapper文件路径,要带后缀名和/ 其他用java包名-->
<mappers>
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
</mappers>
</configuration>
3、在java包下编写mapper.xml配置文件
[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可以自定义,用测试类的时候注意这个属性namespace的值-->
<mapper namespace="org.mybatis.example.BlogMapper">
<select id="selectBlog" resultType="Blog">
select * from Blog where id = #{id}
</select>
</mapper>
4、编写测试代码
一、加载配置
InputStream in=Resources.getResourceAsStream("配置文件路径字符串");
二、获取SqlSessionFactory
SqlSessionFactory sf=SqlSessionFactoryBuilder.build(in);
三、获取SqlSession对象
SqlSession sq=sf.open();
四、执行mapper配置文件的SQL语句
List<Dept> list=sq.SelectList("命名空间+id","参数");
五、关闭sqlsession流
sq.open();
3、测试三个常用的方法
SelectOne(''):返回一行记录,可传参,没有记录返回null。
SelectList(''):返回多行记录,可传参,没有记录返回空List集合。
SelectMap('命名空间+id','Key'):返回Map集合多行记录,没有返回空Map集合。
4、log4j日志输出文件
1、log4j文件是apache下的开源jar包。要导入(log4j.jar和log4j.api.jar)。
2、log4j.xml要与mybatis配置文件放在一起(src目录下)。
3、DEBUG(人为调试信息)、INFO(普通信息)、WARN(警告)、ERROR(错误)和FATAL(系统错误)
这五个级别是有顺序的,DEBUG < INFO < WARN < ERROR < FATAL,分别用来指定这条日志信息的重
要程度,明白这一点很重要,Log4j有一个规则:只输出级别不低于设定级别的日志信息,假设Logger
级别设定为INFO,则INFO、WARN、ERROR和FATAL级别的日志信息都会输出,而级别比INFO低的
DEBUG则不会输出。
- java类测试打印日志
Logger logger= Logger.getLogger(Class002_ManyParameter.class);
logger.error("错误信息");
logger.warn("警告信息");
logger.info("正常信息");
logger.debug("调试信息");
logger.fatal("系统错误");
- 常见日志输出格式
- mybatis加载log4j(在mybatis核心配置文件中定义)
<!-- settings标签 -->
<settings>
<!-- 设置MyBatis使用log4j日志支持 -->
<setting name="logImpl" value="LOG4J"/>
</settings
5、加载外部配置文件
- 在mybatis核心配置文件中定义(注意定义的位置)
<!-- 加载外部的properties文件 -->
<properties resource="db.properties" />
<!-- 在environments配置使用 -->
<environments default="even">
<environment id="even">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
6、设置别名
- 在mybatis核心配置文件中定义(注意定义的位置)
<typeAliases>
<typeAlias type="com.yjxxt.pojo.User" alias="u"/>
</typeAliases>
<!--[注意]省略alias属性, 表示类别名为类名, 大小写不敏感-->
<!--. 可以通过package标签给整个包下的所有类定义别名,别名为类名-->
<typeAliases>
<package name="com.yjxxt.pojo"/> <!-- 包下所有的类默认类名 -->
</typeAliases>
- Mybatis的内建别名
7、参数查询
一、一个参数查询(可以使用String 、int、Map、List、实体类等)
<select id="queryDeptByLoc" parameterType="String" resultType="dept">
select * from dept where loc=#{loc}
</select>
- 测试代码
List<Dept> depts= sqlSession.selectList("com.msb.mapper.DeptMapper.queryDeptByLoc","NEW YORK");
depts.forEach(System.out::println);
- map传参(多个参数传值)
<select id="queryBySal" parameterType="map" resultType="emp">
select * from emp where ename=#{ename} and sal=#{sal}
</select>
- 测试代码(多个参数传值)
Map<String,Object> maps=new HashMap<>();
maps.put("ename","SMITH");
maps.put("sal",2000);
Emp emp1 = sqlSession.selectOne("com.msb.mapper.EmpMapper.queryBySal", maps);
System.out.println(emp1);
- List传参
<select id="insertEmp" parameterType="list">
insert into emp(empno,ename,job,hiredate,sal,deptno) values
<foreach open="(" collection="list" item="item" separator="," close=")">
#{item}
</foreach>
</select>
- 测试代码
List<Object> lists=new ArrayList<>();
lists.add(9000);
lists.add("SMITH");
lists.add("SALESMAN");
lists.add(new Date());
lists.add(5000);
lists.add(30);
int insert = sqlSession.insert("com.msb.mapper.EmpMapper.insertEmp", lists);
System.out.println("执行返回行数:"+insert);
- Date型传值
<update id="updateHireDate" parameterType="date">
update emp set hiredate=#{hiredate} where empno=8888
</update>
- 测试代码
//要先把字符日期型转换成Date型
DateTimeFormatter df=DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime dt=LocalDateTime.parse("2000-01-16 14:25:30",df);
Instant instant = dt.atZone(ZoneId.systemDefault()).toInstant();
Date date = Date.from(instant);
System.out.println(dt);
int count = sqlSession.update("com.msb.mapper.EmpMapper.updateHireDate", date);
System.out.println(count);
二、多个形参传入
- 第一种方法(推荐使用)
//使用@Param("别名"):设置形参的别名,然后与mapper文件的#{别名}一一对应
List<Emp> queryEmpByColumn(@Param("deptno") int deptno,@Param("sal") double sal);
//mapper文件
<select id="queryEmpByColumn" resultType="emp">
select * from emp where deptno=#{deptno} and sal>#{sal}
</select>
- 第二种方法
//直接在mapper文件中#{}占位符中直接写arg0,arg1,arg2....或者param1,param2,param3...
<select id="queryEmpByColumn" resultType="emp">
select * from emp where deptno=#{param1} and sal>#{param2}
</select>
//直接传入参数,会根据参数的位置自动赋值到对应的占位符,若是param从左往右param1开始赋值
//若是arg从左往右arg0开始赋值自动传值
List<Emp> queryEmpByColumn(int deptno,double sal);
8、返回值类型
- map返回值
<select id="selectByMap" resultType="map">
select * from emp
</select>
- 测试代码
//返回Map类型 显示以字段为key 记录为value 返回多行数据 比如 empno=8888,ename=“张三”
List<Map<String,Object>> list = sqlSession.selectList("com.msb.mapper.EmpMapper.selectByMap");
list.forEach(System.out::println);
- list返回值(模糊查询)
<select id="selectByLike" resultType="emp" parameterType="string">
select * from emp where ename like '%'||#{word}||'%'
</select>
- 测试代码
System.out.println("**********模糊查询***************************");
List<Object> list1 = sqlSession.selectList("com.msb.mapper.EmpMapper.selectByLike", "A");
list1.forEach(System.out::println);
- List-Map返回值
<select id="selectByLike" resultType="emp" parameterType="string">
select * from emp where ename like '%'||#{word}||'%'
</select>
- 测试代码
System.out.println("**********模糊查询***************************");
List<Map<String,Object>> list1 = sqlSession.selectList("com.msb.mapper.EmpMapper.selectByLike", "A");
list1.forEach(System.out::println);
9、事务
默认使用jdbc事务,事务默认是关闭,也是要手动提交事务,可以通过SqlSession.commit()进行事务提交。也可以通过sqlSessionFactory.openSession(true)开启自动提交事务。
10、接口绑定方案
- 方案一
<!--
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">
<!-- 规范写法
1、接口与SQL映射文件名字保持一致,并放在同一包下
2、SQL映射文件的namespace定义为与之接口全路径名一致。
3、每个id要与接口抽象方法名要一样。
-->
<!--mapper在核心配置文件中注册-->
<mappers>
<!--第一种引用资源路径文件-->
<!-- <mapper resource="com/msb/mapper/EmpMapper.xml"/>-->
<!--接口绑定方案 绑定指定的xml文件-->
<!-- <mapper class="com.msb.mapper.EmpMapper"/>-->
<!--扫描包下的所有mapper xml文件-->
<package name="com.msb.mapper"/>
</mappers>
- 测试类
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
//注意getMapper(接口类.class) 然后调用接口中的方法
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
List<Emp> emps = mapper.queryAll();
emps.forEach(System.out::println);
System.out.println("***************************************");
Emp emp = mapper.queryEmpById(8888);
System.out.println(emp);
System.out.println("***************************************");
List<Emp> emps1 = mapper.queryEmpByColumn(30,3000);
emps1.forEach(System.out::println);
- 方案二
/**1、在接口抽象方法上直接使用注解
2、不用写xml文件,只需要在核心配置文件进行配置
3、单表的简单的增删改查可以使用注解开发,如果是多表联合查询,建议使用xml文件配置,更加直观。
**/
public interface AnnoTationMapper {
//模拟注解开发
@Select("select * from emp")
List<Emp> findAll();
@Select("select * from emp where empno=#{empno}")
Emp findByEmpNo(int empNo);
@Insert("insert into emp values(#{empno},#{ename},#{job},#{mgr},# {hiredate},#{sal},#{comm},#{deptno})")
int saveEmp(Emp emp);
@Update("update emp set ename=#{ename},job=#{job} where empno=#{empno}")
int updateByNo(@Param("empno") int empno,@Param("ename") String ename,@Param("job") String job);
@Delete("delete from emp where empno=#{empno}")
int deleteByNo(@Param("empno") int empno);
}
- 测试类
//测试注解开发
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
AnnoTationMapper mapper = sqlSession.getMapper(AnnoTationMapper.class);
System.out.println("**********查询所有emp表信息*****************");
List<Emp> all = mapper.findAll();
all.forEach(System.out::println);
System.out.println("**********根据empno查询雇员*****************");
Emp emp = mapper.findByEmpNo(8888);
System.out.println(emp);
11、动态SQL
一、if标签和where标签
<!--
动态SQL使用场景
<if test="">
<where>相当于where 1=1 可以自动屏蔽and关键词
业务设置:
dname和deptno为空 查询全部数据
dname不为空,deptno为0 按照dname查询
dname不为空 deptno也不是0 按照dname和deptno一起查询。
-->
<select id="selectByDnameOrDeptno" resultType="emp">
select * from emp
<where>
<if test="ename!=null and ename!=''">
and ename=#{ename}
</if>
<if test="deptno!=0">
and deptno=#{deptno}
</if>
</where>
</select>
- 测试代码
List<Emp> emps2 = mapper.selectByDnameOrDeptno("SMITH", 20);
emps2.forEach(System.out::println);
二、Choose标签
<!--
动态SQL使用场景
<choose> 相当于java中的switch
<when> 相当于java中的case
<otherwise> 相当于java中的default
业务设置:如果ename和empno都存在 优先查询empno
如果empno不存在 优先查询ename
如果都不存在 查询全部
-->
<select id="selectByEnameAndEmpno" resultType="emp">
select * from emp
<where>
<choose>
<when test="empno!=0">
and empno=#{empno}
</when>
<when test="ename!=null and ename!=''">
and ename=#{ename}
</when>
<otherwise>
and 1=1
</otherwise>
</choose>
</where>
</select>
三、Set标签
<!--
动态SQL使用场景
<set> 相当于update中的set语句 它可以自动处理逗号
常与update使用
-->
<update id="updateByColumn">
update emp
<set>
<if test="ename!=null and ename!=''">
ename=#{ename}
</if>
<if test="sal!=0">
,sal=#{sal}
</if>
</set>
<where>
<if test="empno!=0">
empno=#{empno}
</if>
</where>-->
</update>
四、Trim标签
<!--
测试Trim标签 这个是通用标签 可以用于任何标签的使用
prefix:表示前缀使用标签
prefixOverrides 前缀覆盖标点
suffix:表示后缀使用标签
suffixOverrides:后缀覆盖值
-->
<update id="updateByColumn" >
update emp
<trim prefix="set" prefixOverrides="," >
<if test="ename!=null and ename!=''">
ename=#{ename}
</if>
<if test="sal!=0">
,sal=#{sal}
</if>
</trim>
<trim prefix="where" prefixOverrides="and">
empno=#{empno}
</trim>
</update>
五、Bind标签
!--
bind对sql字符串值进行加工 name表示占位符字段名(ename) value表示对ename进行处理
一般用于模拟模糊查询
-->
<select id="selectByLike" resultType="emp" parameterType="string">
select * from emp
<if test="ename!=null and ename!=''">
<where>
<bind name="ename" value="'%'+ename+'%'"/>
and ename like #{ename}
</where>
</if>
</select>
六、Foreach标签
<!--
使用数组参数 插入一行记录
collection:表示要迭代的数组或者集合
open:开始的字符
separator:迭代值的分隔符
item:迭代的变量
close:结束的字符
-->
<insert id="insertByLine" parameterType="object[]" >
insert into emp
<trim prefix="values">
<foreach collection="objects" open="(" separator="," item="item" close=")">
#{item}
</foreach>
</trim>
</insert>
七、SqL标签和Include标签
<!--
SqL标签表示SQL块
include表示引入SQl块 可以重用 防止SQL语句冗余编写
-->
<sql id="common">
ename,job,mgr,hiredate
</sql>
<select id="selectByEmpToColumn" resultType="emp">
select <include refid="common"/> from emp
</select>
12、实体类属性名和数据库字段名不一致解决方案
- 第一种解决方案
<!--
实体类属性:no name loc
数据库字段名:deptNo dname loc
id:表示对主键字段的数据表映射。
result:对非主键字段的数据表映射。
column:查询数据库表中的字段名
property:java实体类中的属性名
如果数据库的字段名与实体类的属性名一致,会自动进行映射。
-->
<resultMap id="selectAlias" type="DeptDemo">
<id column="deptno" property="no"/>
<result column="dname" property="name"/>
<result column="loc" property="loc"/>
</resultMap>
<select id="findAll" resultMap="selectAlias">
select * from dept
</select>
- 第二种解决方案
<!--
编写的sql语句字段别名与java实体类中的属性名一致,mybatis会自动根据别名进行数据表映射.
实体类属性:no name loc
数据库字段名:deptNo dname loc
-->
<select id="findAll" resultMap="selectAlias">
select deptno as no,dname as name,loc as loc from dept
</select>
13、多表查询
一、一对一查询
业务场景:查询每个雇员的信息及其雇员部门信息
- mapper文件
<!--
首先要在主表实体类中定义好从表映射的实体类
一对一查询
查询雇员信息以及雇员所在部门信息
查询的时候以emp表为主表 type:显示主表的实体类
property:实体类的属性值
column:数据库字段名
id:主键字段数据表映射
result:非主键字段表映射
由于考虑到一对一查询的一个实体类对统一个实体类所以使用association建立联系
association中的property表示实体类对象名 javaType:表示实体类的java对象类型
association标签中的是从表数据库字段与实体类的映射。
-->
<resultMap id="Emp_Dept" type="emp">
<id property="empno" column="empno"/>
<result property="ename" column="ename"/>
<result property="job" column="job"/>
<result property="mgr" column="mgr"/>
<result property="hiredate" column="hiredate"/>
<result property="sal" column="sal"/>
<result property="comm" column="comm"/>
<result property="deptno" column="deptno"/>
<association property="ppdept" javaType="dept">
<id column="deptno" property="deptno"/>
<result column="dname" property="dname"/>
<result column="loc" property="loc"/>
</association>
</resultMap>
<select id="selectEmpDept" resultMap="Emp_Dept">
select * from emp e left outer join dept d on e.deptno=d.deptno
</select>
</mapper>
二、一对多查询
业务场景:查询每个部门所有的雇员信息
- mapper文件
<!--
一个部门有多个员工
一对多关系
下面的id type property column属性名的意思与上述一样
不同的因为可能会查询出多个关联雇员信息 所以使用 collection集合标签
collection中的property集合对象名 javaType:集合名(list、map、set等) ofType:集合中泛型对象类型 标签中的映射就是对集合中泛型对象类型的属性和数据库字段名映射。
-->
<resultMap id="Dept_Emp" type="dept">
<id property="deptno" column="deptno"/>
<result property="dname" column="dname"/>
<result property="loc" column="loc"/>
<!--javaType集合对象 比如List Map等
ofType表示集合中的对象
-->
<collection property="lists" javaType="list" ofType="emp">
<id property="empno" column="empno"/>
<result property="ename" column="ename"/>
<result property="job" column="job"/>
<result property="mgr" column="mgr"/>
<result property="hiredate" column="hiredate"/>
<result property="sal" column="sal"/>
<result property="comm" column="comm"/>
<result property="deptno" column="deptno"/>
</collection>
</resultMap>
<select id="selectByDept" resultMap="Dept_Emp">
select * from dept d left outer join emp e on d.deptno=e.deptno
</select>
14、注解开发
注解开发不用编写mapper.xml文件,对于单个数据表查询非常简便,但是对于多表比较繁琐,不直观,容易写错,所以多表查询推荐xml文件配置编写SQL。
@Select("查询SQL"):进行数据表的查询操作。
@Update("更新SQL"):进行数据表的更新操作。
@Delete("删除SQL"):进行数据表的删除操作。
@Insert("插入SQL"):进行数据表的记录插入操作。
多表查询的SQL注解
@Results: 类似于resultMap标签。
@Result: 类似resultMap的子标签result,若是设置id=true 表示该字段是主键。
@One: 类似于association标签。
@Many: 类似于collection标签。
所有的注解都在接口的对应的抽象方法上定义。
- 测试代码
public interface AnnoTationMapper {
//模拟注解开发
@Select("select * from emp")
List<Emp> findAll();
@Select("select * from emp where empno=#{empno}")
Emp findByEmpNo(int empNo);
@Insert("insert into emp values(#{empno},#{ename},#{job},#{mgr},#{hiredate},#{sal},#{comm},#{deptno})")
int saveEmp(Emp emp);
@Update("update emp set ename=#{ename},job=#{job} where empno=#{empno}")
int updateByNo(@Param("empno") int empno,@Param("ename") String ename,@Param("job") String job);
@Delete("delete from emp where empno=#{empno}")
int deleteByNo(@Param("empno") int empno);
}
15、缓存
- 一级缓存
1、mybatis默认开启一级缓存,一级缓存的作用域在每一个sqlsession中,一个sqlsession有一个一级缓存,每个sqlsession的一级相互隔离。
2、mybatis一级缓存进行增删改操作的时候会刷新一次。
3、在一sqlsession生命周期中有效,sqlsession关闭,缓存清空。
4、在同一个sqlsession中,mybatis会把执行的方法和参数通过算法生成缓存的键值,将键值对放入一个Map中,如果后续的键值一样,则直接从Map中获取数据。
5、可通过配置在查询前清空缓存;flushCache="true"
<select id="queryAll" resultType="emp" flushCache="true">
select * from emp
</select>
- 二级缓存
1、进程级别的缓存,sqlSessionFactory的缓存。
2、在一个SqlSessionFactory生命周期有效,可以在多个SqlSession中共享。
3、默认是关闭二级缓存,需要使用的时候,要为某个命名空间开启二级缓存(在mapper文件配置cache)
开启二级缓存的时候,要求实体类进行序列化。
<mapper namespace="com.msb.mapper.EmpMapper">
<!-- 开启二级缓存 flushInterval默认是毫秒 30分钟刷新一次-->
<cache flushInterval="30*60*1000"/>
<mapper/>
4、由于进行增删改的时候会刷新缓存,所以如果是查询比较多的时候可以开启二级缓存,如果是增删改操作比较多的时候,不建议开启。
5、二级缓存是以namespace为单位,不同namespace之间使用缓存互不干扰,如果进行增删改查询,整个namespace的查询都要刷新一次。
6、最好在单表操作的namespace使用缓存,对该表的操作都在namespace中,否则会出现数据不一致的情况。
7、应用场景:对查询实时性不高的操作使用,降低数据库访问量,如:电话账单查询。
可以根据需要设置刷新间隔时间flushinterval:刷新间隔时间。
标签:ename,框架,mapper,empno,查询,emp,mybatis,select
From: https://www.cnblogs.com/smallzengstudy/p/17626479.html