mybatis中,封装了一个sqlsession 对象(里面封装有connection对象),由此对象来对数据库进行CRUD操做。php
mybatis有一个配置的xml,用于配置数据源、映射Mapping,xml的文件名能够任取,为了方便,咱们仍是起mybatis-config.xmlhtml
咱们读取此配置的xml,得到一个sqlsession,以后由此对象类进行数据库的CRUD操做java
Reader reader = Resources.getResourceAsReader("mybatis-config.xml"); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader); SqlSession sqlSession = factory.openSession();
<?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 resource="jdbc.properties"/> <!-- 配置数据源环境 --> <environments default="development"> <environment id="development"> <!-- 数据库事务管理类型 --> <transactionManager type="JDBC"/> <!-- 数据源,type=pooled 说明是使用链接池方式,能够节省资源 --> <dataSource type="POOLED"> <!-- 调用资源文件里的用户信息--> <property name="driver" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </dataSource> </environment> </environments> </configuration>
Dao类中每次进行CRUD操做,都要执行一次openSession方法
来得到SqlSession对象
,形成资源的浪费和代码的重复mysql
因此,和以前的JdbcUtil
工具类同样,咱们也定义定义一个工具类MyBatisUtil
,用来返回SQLSession对象
git
static SqlSessionFactory sqlSessionFactory = null; static { try { // 加载mybatis配置文件,并建立SqlSessionFactory实例 String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); //这个build方法能够接受几种不一样的参数,如Reader/InputSteam等 sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { } } public static SqlSession getSqlSession() { return sqlSessionFactory.openSession(); } public static void closeSqlSession(SqlSession sqlSession){ if (sqlSession != null) { sqlSession.close(); } }
mapper文件放在了resources下面程序员
Mybatis中,sql语句则是写在了xml文件中,这些xml文件也称为mapper映射文件github
mapper标签若是带有xmln属性,IDEA会报解析xml错误,得把xmln属性删除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"> <!-- namespace: 命名空间,用于标识每个Mapper XML文件中的语句,预防在不一样的Mapper XML文件中存在相同的语句ID --> <mapper namespace="employeeMapper"> <!-- resultType: 也称为自动映射,只有在表的列名与POJO类的属性彻底一致时使用,会比较方便,全类名 --> <select id="selectAll" resultType="com.wan.bean.Employee"> select * from employee </select> </mapper>
<?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> <!-- 省略数据源配置--> <mappers> <mapper resource="com/wan/mapping/employeeMapper.xml"/> <!--若是还有mapper,则继续添加 --> </mappers> </configuration>
SqlSession sqlSession = MybatisUtil.getSqlSession(); // 调用语句,若是有参数,传入参数 //参数为命名空间namespace+id,执行xml中的sql语句 List<Employee> list = sqlSession.selectList("employeeMapper.selectAll");
PS:若是是插入、更新和删除操做,还须要提交操做,默认是不会自动提交的数据库
sqlSession.commit();
<select id="selectAll" resultType="com.wan.bean.Employee"> select * from employee </select>
resultType
属性须要全类名,咱们可使用typeAliases
标签来简化输入数组
typeAliases
标签须要在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> <properties resource="jdbc.properties"/> <!--指定一个bean包 --> <typeAliases> <package name="com.wan.bean"/> </typeAliases> <!--省略配置数据源等--> </configuration>
以后咱们的mapper文件中就能够这样写
<!--resultType就能够不用写全包名 --> <select id="selectAll" resultType="Employee"> select * from employee </select>
我这里只介绍用法,详解请看下面的参考连接
1. 文件路径注册
<mappers> <mapper resource="com/wan/mapper/EmployeeMapper.xml" /> </mappers>
2. 包名扫描注册
<mappers> <package name="com.wan.mapper" /> </mappers>
使用这种,必须保证xxxMapper.java和xxxMapper.xml二者名字如出一辙!并且是要在同一包里
3. 类名注册
<mappers> <mapper class="com.shizongger.chapter2.mapper.UserMapper" /> </mappers>
4. url注册
<mappers> <mapper url="file:/home/shizongger/workspace/Chapter3/src/com/shizongger/chapter2/mapper/RoleMapper.xml" /> </mappers>
方法名 | 说明 |
---|---|
insert | 插入 |
delete | 删除 |
update | 更新 |
selectOne | 查找单行结果,返回一个Object |
selectList | 查找多行结果,返回一个List |
使用和以前同样,第一个参数传入一个namespce+id,就能够找到指定的mapper文件里面的sql语句,并执行
Employee中,属性名和表的列名对应
<select id="selectAll" resultType="Employee"> select * from employee </select>
若是属性和表的列名不一致,可使用列名映射resultMap标签,(也就是自动转为别名)
<!--type也是须要全包名,因为以前定义的别名,因此就能够不写--> <resultMap id="baseResultMap" type="Employee"> <!--使用映射,把对应的列名映射为对应的属性 --> <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"/> </resultMap> <!--引用上面定义的resultMap--> <select id="selectAll" resultMap="baseResultMap"> select * from employee </select>
带条件查询
<select id="selectById" parameterType="int" resultMap="baseResultMap"> <!-- 若是参数类型是简单的基本或者包装类型,#{} 里面的能够任取,都是能够得到参数 --> select * from EMPLOYEE where EMPNO=#{id} </select> //使用 Employee e = sqlsession.selectOne("employeeMapper.selectById",7369);
上面的select语句至关于一个预编译语句
String s = "SELECT * FROM employee WHERE empno=?"; PreparedStatement ps = conn.prepareStatement(s); ps.setInt(1,empno);
多条件查询
可使用where标签,固然,以前的单条件也可使用where标签,where标签好处是会自动删除多余的and
<select id="selectSelective" parameterType="Employee" resultMap="baseResultMap"> select * from EMPLOYEE <where> <!--自动删除多余的and --> <!--#至关于从传入的bean对象(Employee)中经过getDeptno方法得到属性值 --> and deptno=#{deptno} and sal>=2000 </where> </select>
大小比较条件
条件中有大小比较,<
号得经过CDATA存放条件
<select id="selectSelective" parameterType="Employee" resultMap="baseResultMap"> select * from EMPLOYEE <where> <!--loSal为Employee的一个属性,#{loSal}至关因而经过Employee对象的get方法来得到loSal的属性值 --> and SAL>=#{loSal} <!--CDATA中的数据不会被解析器解析 --> <![CDATA[ and SAL<=#{hiSal} ]]> </where> </select>
#与$区别:
${}
用在咱们可以肯定值的地方,也就是咱们程序员本身赋值的地方。 而#{}
通常用在用户输入值的地方!!
参考: MyBatis中#{}和${}的不一样和${}的妙用
模糊查询:
模糊查询中须要使用%
等通配符,咱们能够在xml中定义好,自动拼接通配符
<select id="selectSelective" parameterType="Employee" resultMap="baseResultMap"> select * from EMPLOYEE <where> <if test="ename != null"> <!--使用bind标签,设置格式,自动拼接通配符 --> <bind name="pattern" value="'%' + ename + '%'"/> and ENAME like #{pattern} </if> </where> </select>
动态查询
Mybatis中提供了if
标签用来实现动态查询,和JSTL标签库使用相似
<select id="selectSelective" parameterType="Employee" resultMap="baseResultMap"> select * from EMPLOYEE <where> <!--#{ename}实际上是经过Employee类中的get方法来得到对象的ename属性值 --> <if test="ename != null"> and ename=#{ename} </if> <if test="job != null and job.trim().length>0"> and JOB=#{job} </if> <if test="deptno != null"> and DEPTNO=#{deptno} </if> </where> </select>
主键为序列
某个主键是由oracle中的序列生成的
<insert id="insert_1" parameterType="Employee"> <!-- keyProperty: 表示将从序列得到的值赋予实体的哪一个属性 order: 表示主键值生成的方式,可取值:BEFORE | AFTER 因为不一样的数据库对插入的数据时主键生成方式是不一样,例如: mysql and ms server: 主键生成方式为后生成方式。 oracle: 主键生成方式预生成. --> <!--调用数据库中的序列,并赋值给传入的Employee对象的empno属性 --> <selectKey keyProperty="empno" resultType="integer" order="BEFORE"> select EMP_SEQ.nextval from dual </selectKey> <!-- 若是使用这种整表插入的方式,那当数据库表的某些列能够为空值时,我将要告诉底层的JDBC驱动如何处理空值的状况,这不是mybatis所须要的, 而是底层有些JDBC驱动所需的特性,实际上就是让JDBC驱动去调用PrepareStatement.setNull()来设置空值 --> <!--若是是经常使用的数据类型int,date等,jdbcType能够省略不写 --> insert into EMPLOYEE values (#{empno},#{ename},#{job},#{mgr,jdbcType=INTEGER},#{hiredate,jdbcType=DATE},#{sal,jdbcType=DOUBLE},#{comm,jdbcType=DOUBLE},#{deptno,jdbcType=INTEGER}) </insert>
复用sql语句
把insert要插入的列名和数值写在sql标签里,以后方便重用,以后重用的时候须要使用include
子标签拼接sql语句
<!--insert into employee(ENAME,JOB..) values(xx,xx) --> <!--(ENAME,JOB..) --> <sql id="insert_set_column"> <!-- suffixOverrides属性,会自动把多余的“,”删除 --> <trim prefix="(" suffix=")" suffixOverrides=","> empno, <if test="ename != null">ENAME,</if> <if test="job != null">JOB,</if> <if test="mgr != null">MGR,</if> <if test="hiredate != null">HIREDATE,</if> <if test="sal != null">SAL,</if> <if test="comm != null">COMM,</if> <if test="deptno != null">DEPTNO,</if> </trim> </sql> <!--(xx,xx,xx) --> <sql id="insert_values"> <trim prefix="values(" suffix=")" suffixOverrides=","> #{empno}, <if test="ename != null">#{ename},</if> <if test="job != null">#{job},</if> <if test="mgr != null">#{mgr},</if> <if test="hiredate != null">#{hiredate},</if> <if test="sal != null">#{sal},</if> <if test="comm != null">#{comm},</if> <if test="deptno != null">#{deptno},</if> </trim> </sql> <insert id="insert_2" parameterType="Employee"> <selectKey keyProperty="empno" resultType="integer" order="BEFORE"> select EMP_SEQ.nextval from dual </selectKey> insert into EMPLOYEE <!--拼接sql --> <include refid="insert_set_column"/> <include refid="insert_values"/> </insert>
<update id="update_1" parameterType="Employee"> update EMPLOYEE <set> <if test="ename != null and ename.trim().length>0">ENAME=#{ename},</if> <if test="job != null and job.trim().length>0">JOB=#{job},</if> <if test="mgr != null">MGR=#{mgr},</if> <if test="hiredate != null">HIREDATE=#{hiredate},</if> <if test="sal != null">SAL=#{sal},</if> <if test="comm != null">COMM=#{comm},</if> <if test="deptno != null">DEPTNO=#{deptno},</if> </set> <!-- <where>若是带多条件的更依然可使<where>元素动态生成where子句</where> --> where EMPNO=#{empno} </update>
<update id="update_2" parameterType="Employee"> update EMPLOYEE <trim prefix="set" suffixOverrides=","> <if test="ename != null and ename.trim().length>0">ENAME=#{ename},</if> <if test="job != null and job.trim().length>0">JOB=#{job},</if> <if test="mgr != null">MGR=#{mgr},</if> <if test="hiredate != null">HIREDATE=#{hiredate},</if> <if test="sal != null">SAL=#{sal},</if> <if test="comm != null">COMM=#{comm},</if> <if test="deptno != null">DEPTNO=#{deptno},</if> </trim> <!-- <where>若是带多条件的更依然可使<where>元素动态生成where子句</where> --> where EMPNO=#{empno} </update>
<delete id="delete" parameterType="Employee"> delete EMPLOYEE EMPNO=#{empno} <!--条件多的话也可使用<where>...</where> --> </delete>
咱们以前,上面都是在Dao类中写上一段sqlsession.selectOne/selectList,仍是比较麻烦
因此mybatis提供了一种简单的方法,使用动态代理(接口类)能够简化步骤
Mybatis中有这样的约定:
知足上面的条件,Mybatis就会将接口类中的方法和mapper中的sql语句一一对应起来,而不须要再次新建一个Dao,在Dao类里面编写方法
具体步骤:
1. 实体类编写
2. 新建接口类
若是方法的返回值为void,则mapper中就不须要定义resultType属性
若是方法返回值是List,mapper中的resultType为泛型T
package com.wan.mapping; import com.wan.bean.Employee; import java.util.List; /** * @author StarsOne * @date Create in 2019/9/16 0016 20:38 * @description */ public interface EmployeeMapper { List<Employee> selectAll(); }
2. 编写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"> <mapper namespace="com.wan.mapping.EmployeeMapper"> <!--特例:返回值为list,resultType=bean类--> <select id="selectAll" resultType="Employee" > select * from employee </select> </mapper>
3. 注册mapper
这里咱们因为使用了package注册mapper,必定保证xxmapper.java和xxmapper.xml两个名字相同,大小写都要同样
保证Mapper.xml和接口的那个Mapper在相同的包路径,在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> <!--省略数据源配置 -->... <!-- 注册SQL映射文件,在这些文件中写SQL语句 --> <mappers> <!--指定整个包中的所有Mapper --> <package name="com.wan.mapper"/> </mappers> </configuration>
4. 使用
使用仍是和以前同样,得到SqlSession对象,此对象有个getMapper方法,把接口类传入,就能够回调接口的方法了
Reader reader = Resources.getResourceAsReader("mybatis-config.xml"); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader); SqlSession sqlSession = factory.openSession(); EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class); List<Employee> employees = mapper.selectAll();
接口类中的方法名与EmployeeMapper.xml中的对应
使用:
EmployeeMapper mapper = sqlsession.getMapper(EmployeeMapper.class); mapper.selectById(7369);
Mybatis中提供了foreach标签,用于遍历
若是方法参数传入了一个List,可使用此标签遍历,例子以下:
<!--至关于select * from employee where job in (...)) --> <select id="selectByJobs" parameterType="list" resultMap="baseResultMap"> select * from EMPLOYEE <where> <foreach item="job" collection="list" open="JOB IN(" close=")" separator=","> #{job} </foreach> </where> </select>
foreach标签的属性主要有 item,index,collection,open,separator,close,使用和JSTL标签里面的foreach标签差很少
属性名 | 说明 |
---|---|
item | 表示集合中每个元素进行迭代时的别名 |
index | 指定一个名字,用于表示在迭代过程当中,每次迭代到的位置, |
open | 表示该语句以什么开始, |
separator | 表示在每次进行迭代之间以什么符号做为分隔 符, |
close | 表示以什么结束。 |
关键属性:collection
参考:mybatis 中 foreach collection的三种用法
使用ThreadLocal对象,保证每一个线程取出的SqlSession是同一个对象
方法 | 说明 |
---|---|
void set(Object value) | 设置当前线程的线程局部变量的值。 |
public Object get() | 该方法返回当前线程所对应的线程局部变量。 |
public void remove() | 将当前线程局部变量的值删除,目的是为了减小内存的占用,该方法是JDK 5.0新增的方法。 |
protected Object initialValue() | 返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。 |
static ThreadLocal<SqlSession> threadLocal = new ThreadLocal<SqlSession>(); //设置 threadLocal.set(sqlsession); //取出 SqlSession s = threadLocal.get();
<!-- 结果集映射: 列《》属性名 --> <resultMap id="baseResultMap" type="Employee"> <!-- 专门映射主键列 --> <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"/> </resultMap> <!-- 扩展另外一个结果映射 --> <resultMap id="extendBaseResultMap" type="Employee" extends="baseResultMap"> <association property="department" javaType="Department"> <!-- 关联的嵌套结果 --> <id property="deptno" column="DEPTNO"/> <result property="dname" column="DNAME"/> <result property="location" column="LOC"/> </association> </resultMap> <!-- 1.嵌套结果(推荐使用) 优势:性能好,一条语句把全部实体的数据彻底查询出来。 缺点:对SQL编写的要求高了,由于涉及多表链接查询 --> <select id="selectById" resultMap="extendBaseResultMap" parameterType="int"> select e.EMPNO, e.ENAME, e.JOB, e.MGR, e.HIREDATE, e.SAL, e.COMM, d.DEPTNO, d.DNAME, d.LOC from EMPLOYEE E inner join DEPARTMENT D on E.DEPTNO = D.DEPTNO where E.EMPNO=#{id} </select> <!-- 2. 嵌套查询 优势:编写SQL简单,无需作多表的链接查询;关联的实体经过单独的SQL语句查询并单独封装。 缺点:执行了N+1条件语句。性能差 --> <resultMap id="extendBaseResultMap_2" type="Employee" extends="baseResultMap"> <association property="department" column="DEPTNO" select="selectDepartmentById" /> </resultMap> <select id="selectDepartmentById" parameterType="int" resultType="Department"> select deptno, dname, loc as location from DEPARTMENT where DEPTNO=#{id} </select> <select id="selectById_2" resultMap="extendBaseResultMap_2" parameterType="int"> select e.EMPNO, e.ENAME, e.JOB, e.MGR, e.HIREDATE, e.SAL, e.COMM, e.DEPTNO from EMPLOYEE E where E.EMPNO=#{id} <!-- or e.empno=7902 or e.empno=7844 --> </select>
分页的话,像之前那样使用三层嵌套查询也能够实现。
不过,有开发者为Mybatis开了个一个插件PageHelper,能够用来更为简单地使用分页查询
1.添加jar包
<dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <!--自动下载最新版本 --> <version>REALSE</version> </dependency>
2.配置拦截器插件
<!-- plugins在配置文件中的位置必须符合要求,不然会报错,顺序以下: properties?, settings?, typeAliases?, typeHandlers?, objectFactory?,objectWrapperFactory?, plugins?, environments?, databaseIdProvider?, mappers? --> <plugins> <!-- com.github.pagehelper为PageHelper类所在包名 --> <plugin interceptor="com.github.pagehelper.PageInterceptor"> <!-- 使用下面的方式配置参数,后面会有全部的参数介绍 --> <property name="param1" value="value1"/> </plugin> </plugins>
3.代码使用
只有在查询以前调用过startPage
或者是offsetPage
方法,后面的查询出来的List结果就会进行分页查询
下面的两个都是查询第一页,每一页有10条数据
//第二种,Mapper接口方式的调用,推荐这种使用方式。 PageHelper.startPage(1, 10); List<Employee> employees = employeeMapper.selectAll(); //第三种,Mapper接口方式的调用,推荐这种使用方式。 PageHelper.offsetPage(1, 10); List<Employee> employees = employeeMapper.selectAll();
这里提一下,这个插件还带有一个PageInfo
类,里面有能够记录各类信息
刚开始,我觉得是和我以前本身封装的Page同样,详情请看Jsp学习笔记(4)——分页查询
可是,其实不同的,这个PageInfo就是一个封装而已,只是用来存放数据而已,里面有各类信息
属性 | 说明 |
---|---|
pageNum | 当前页号(第几页) |
pageSize | 每页的显示的数据个数 |
size | 当前页的显示的数据个数 |
startRow | 当前页面第一个元素在数据库中的行号 |
endRow | 当前页面最后一个元素在数据库中的行号 |
pages | 总页数 |
prePage | 上一页的页号 |
nextPage | 下一页页号 |
isFirstPage | 是否为第一页 |
isLastPage | 是否为最后一页 |
hasPreviousPage | 是否有前一页 |
hasNextPage | 是否有下一页 |
navigatePages | 导航页码数 |
navigatepageNums | 全部导航页号 |
navigatePages | 导航条上的第一页 |
navigateFirstPage | 导航条上的第一页 |
navigateLastPage | 导航条上的最后一页 |
有个getTotal
方法,能够得到查询结果的总记录数
PageHelper.startPage(1, 10); List<Employee> employees = mapper.selectAll(); PageInfo<Employee> pageInfo = new PageInfo<>(employees);
原文出处:https://www.cnblogs.com/stars-one/p/11537439.html