Mybatis入门知识点
一、MyBatis介绍
1.什么是mybatis
- mybatis封装了几乎所有的jdbc代码和参数的手工设置以及结果集的检索,支持普通的SQL查询、存储过程和高级映射,是一款优秀的持久层框架,使用简单的xml或Java注解做配置和定义映射关系,将Java中的pojos映射成数据库中的记录。
- 实体类–》数据库中的表
- 实体类中的属性—》表中的字段
- 实体类的对象----》表中的记录
2.mybatis的体系结构
-
1.配置加载
- xml配置
- Java代码注解
- mybatis将sql的配置信息加载成为一个个的mapped Statement的对象(包含了传入的参数映射配置、执行的SQL语句、结果映射配置)并将 其存储到内存中
-
SQL解析
- 当api接口层接受到调用时,会接受到传入的SQL的id和传入对象(可以是map/JavaBean或者基本数据类型),mybatis根据id找到对应的mapped Statement对象,并且根据传入的参数对其进行解析,解析后可以得到最终要执行的SQL语句和参数
-
SQL执行
- 把解析出的SQL语句和参数拿到数据库执行,得到操作数据库的结果
-
结果映射
- 将操作数据库的结果按照映射的配置进行转换(可以转换hashmap/JavaBean或基本数据类型),并将最终结果返回
3.mybatis的应用
-
1、主配置文件:指定数据库的连接信息以及设置框架的相关参数。
2、关联映射文件:用于定义SQL语句及相关映射信息。
4.Mybatis框架
- SqlSessionFactoryBuilder:负责加载配置文件,同时创建SqlSessionFactoryBuilder实例
- SqlSessionFactory:每一个Mybatis的应用程序都以一个SqlSessionFactory对象为核心,该对象负责创建SqlSessionFactory
- SqlSession:该对象包含了所有执行SQL操作的方法,用于执行已映射的SQL语句
5.实现分页
-
在使用SqlSession的selectList()方法时,指定一个分页参数器 RowBounds;
-
分页参数器的构造器RowBounds(offset,pageSize);
-
Offset: 抓取记录的起始位置;
-
pageSize: 指定的每页显示的记录数;
-
二、MyBatis实现增删查改功能
1.maven环境搭建
-
创建maven项目完成后,配置pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.qst</groupId> <artifactId>mybatisMaven</artifactId> <version>0.0.1-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.5</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.38</version> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency> </dependencies> <!-- 配置 maven 项目的jdk版本 --> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build> </project>
-
添加相关配置文件
-
把文件mybatis-config.xml放在src/main/resouces下面
-
把文件EmpMapper.xml放在你所用的实体类下面
-
-
- 修改mybatis-config.xml
- 使用EmpMapper.xml
2.创建连接
public class MyUtil {
static SqlSessionFactory sf =null;
static {
//1.定义mybatis-config的文件地址
String conf = "mybatis-config.xml";
Reader read;
try {
read = Resources.getResourceAsReader(conf);//Resources读取配置文件
SqlSessionFactoryBuilder sfb = new SqlSessionFactoryBuilder();//加载配置文件
sf = sfb.build(read);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static SqlSession getSqlSession(){
return sf.openSession();//返回SQL session对象
}
}
3.dao层里的方法
package com.qst.dao;
import java.util.List;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;
import com.qst.pojo.Emp;
import com.qst.util.MyUtil;
public class EmpDao {
//员工信息添加
public void insertEmp(Emp emp){
SqlSession session=MyUtil.getSqlSession();//用于执行已经映射的SQL语句
session.insert("addEmp",emp);
session.commit();//提交
session.close();//记得关闭,以免造成资源的浪费
}
//更新员工信息
public void updateEmp(Emp emp){
SqlSession session=MyUtil.getSqlSession();
session.update("updateEmp",emp);
session.commit();
System.out.println("成功");
session.close();
}
//根据id查询员工信息
public Emp findEmpById(int id){
SqlSession session=MyUtil.getSqlSession();
Emp emp=session.selectOne("selectEmpById",id);
session.close();
return emp;
}
//查询所有员工列表信息
public List<Emp> findEmps(){
SqlSession session=MyUtil.getSqlSession();
List<Emp> emps=session.selectList("findAll");
session.close();
return emps;
}
//分页
public List<Emp> findEmps1(int page,int pageSize){
SqlSession session=MyUtil.getSqlSession();
RowBounds rb=new RowBounds((page-1)*pageSize,pageSize);
List<Emp>emps=session.selectList("findAll2",null,rb);
session.close();
return emps;
}
//根据id删除员工信息
public void deleEmp(int id){
SqlSession session=MyUtil.getSqlSession();
session.delete("delEmpById", id);
session.commit();
System.out.println("删除成功");
session.close();
}
}
4.main方法里的调用
package com.qst.test;
import java.util.List;
import com.qst.dao.EmpDao;
import com.qst.pojo.Emp;
public class Test {
public static void main(String[] args) {
//Emp emp = new Emp();
/*emp.setName("上帝");
emp.setSex("男");
emp.setSalary(50000.0);
emp.setAddress("中山市德创智谷");*/
//emp.setName("神仙");
EmpDao dao = new EmpDao();
//dao.insertEmp(emp);
// Emp emp=dao.findEmpById(12);
// System.out.println(emp);
// emp.setName("美女");
// emp.setSex("女");
// dao.updateEmp(emp);
// List<Emp>emps=dao.findEmps1(2, 2);
// System.out.println(emps);
dao.deleEmp(12);
}
}
5.多条件查询使用map集合
- parameterType的类型为"map"
<select id="findEmpByNameAndSalary" parameterType="map" resultType="com.qst.pojo.Emp">
select * from t_emp where name=#{name} and salary=#{salary}
</select>
-
dao方法
public List<Emp> findEmpByNAndS(Map map){ SqlSession session=MyUtil.getSqlSession(); List<Emp>emps=session.selectList("findEmpByNameAndSalary", map); session.close(); return emps; }
-
主方法
Map map=new HashMap(); map.put("name","文琴麻麻" ); map.put("salary", 10000); List<Emp> emps=dao.findEmpByNAndS(map); System.out.println(emps);
6.当JavaBean属性与列表字段不一致时解决的映射问题
-
Mapper.xml
- 使用resultMap解决
<mapper namespace="/"> <!-- 根据id查找对应的员工信息 --> <select id="selectEmpId" parameterType="int" resultMap="empMap"> select * from t_emp where id=#{id} </select> <resultMap type="com.qst.pojo.Emp2" id="empMap"> <result property="addr" column="address"/> </resultMap> </mapper>
-
dao方法
public Emp2 findEmp(int id){ SqlSession session=MyUtil.getSqlSession(); Emp2 emp=session.selectOne("selectEmpId", id); session.close(); return emp; }
-
测试结果
//JavaBean属性与字段不一致的映射 EmpDao2 ed=new EmpDao2(); Emp2 emp2=ed.findEmp(3); System.out.println(emp2);
7.真分页查询
(1).使用Map实现真分页
-
Mapper.xml
<!-- 真分页实现员工信息列表查询 --> <select id="findPages" parameterType="map" resultType="com.qst.pojo.Emp"> select * from t_emp limit #{offset},#{pageSize} </select>
-
dao
//真分页查询 public List<Emp> findPages(int page,int pageSize){ SqlSession session=MyUtil.getSqlSession(); Map map=new HashMap(); map.put("offset",(page-1)*pageSize); map.put("pageSize", pageSize); List<Emp>emps=session.selectList("findPages", map); return emps; }
-
测试
//真分页 List<Emp>emps=dao.findPages(1, 2); System.out.println(emps);
(二).使用JavaBean实现真分页
- 创建empbean类
-
Mapper.xml
<!-- 使用JavaBean实现分页 --> <select id="findPages" parameterType="com.qst.pojo.EmpBean" resultType="com.qst.pojo.Emp"> select * from t_emp limit #{offset},#{pageSize} </select>
-
dao
//使用JavaBean实现真分页 public List<Emp> findPages(int page,int pageSize){ SqlSession session=DBUtil.getSqlSession(); EmpBean eb=new EmpBean(); eb.setPage(page); eb.setPageSize(pageSize); List<Emp>emps=session.selectList("findPages", eb); return emps; }
-
测试
List<Emp>emps=ed.findPages(3, 2); System.out.println(emps);
8.假分页查询
-
Mapper.xml
<!-- 假分页 --> <select id="findAll2" resultType="com.qst.pojo.Emp"> select * from t_emp </select>
-
dao
//假分页 public List<Emp> findEmps1(int page,int pageSize){ SqlSession session=MyUtil.getSqlSession(); RowBounds rb=new RowBounds((page-1)*pageSize,pageSize); List<Emp>emps=session.selectList("findAll2",null,rb); session.close(); return emps; }
-
main
//假分页 List<Emp>emps=dao.findEmps1(2, 2); System.out.println(emps);
9.真分页和假分页的区别
- 真分页:查询的SQL语句,确定要显示的数量和内容,然后每次都去数据库取出该少量数据
- 假分页:一次性取出所有的数据,然后再显示
- 使用分页参数器 RowBounds;,分页参数器的构造器RowBounds(offset,pageSize);
10.插入数据时的主键回显
-
Mapper.xml
- useGeneratedKeys:是否要主键回显
- keyProperty:要回显的主键
<!-- 员工信息添加,主键的回显 --> <insert id="addEmpInfo" parameterType="com.qst.pojo.Emp" keyProperty="id" useGeneratedKeys="true"> insert into t_emp(name,sex,salary,address)values(#{name},#{sex},#{salary},#{address}) </insert>
-
dao
//插入员工信息,主键回显 public void addEmpInfo(Emp emp){ SqlSession session=MyUtil.getSqlSession(); session.insert("addEmpInfo", emp); session.commit(); session.close(); }
-
测试
emp.setName("唐漂亮"); emp.setSex("女"); emp.setSalary(10000); emp.setAddress("湖南"); dao.addEmpInfo(emp); System.out.println("获取到回显的id="+emp.getId());
三、Mybatis的动态SQL
1.介绍
- 动态SQL是MySQL框架强大的特性之一,在一些组合查询页面,需要根据用户输入的条件生成不同的查询SQL,这在jdbc中需要在代码中拼写SQL,但在Mybatis中可以解决这个问题
- 使用动态SQL元素与jstl相似,它允许我们在xml中去构建不同的SQL语句
2.常用元素
(1)、判断元素:if choose
-
if元素:是简单的条件判断逻辑,满足条件时追加if元素内的SQL,不满足条件时不追加
-
<select id="askEmp" parameterType="map" resultType="com.qst.pojo.Emp"> select * from t_emp where 1=1 <if test="name!=null and name!=''"> and name=#{name} </if> <!-- ps:使用and连接 --> <if test="address!=null and address!=''"> and address=#{address} </if> </select>
//if元素的应用 public List<Emp> askEmp(String name,String address){ SqlSession session=MyUtil.getSqlSession(); Map map=new HashMap(); map.put("name", name); map.put("address", address); List<Emp> emps=session.selectList("askEmp", map); session.close(); return emps; } public static void main(String[] args) { Emp emp = new Emp(); EmpDao dao = new EmpDao(); List<Emp> emps=dao.askEmp3("丁丁", ""); System.out.println(emps); }
-
choose元素:类似if else语句
<!--choose的应用 --> <select id="askEmp2" parameterType="map" resultType="com.qst.pojo.Emp"> select * from t_emp <choose> <when test="name!=null and name!=''"> where name=#{name} </when> <when test="address!=null and address!=''"> where address=#{address} </when> <otherwise> where salary>1000 </otherwise> </choose>
//choose元素的应用 public List<Emp> askEmp2(String name,String address){ SqlSession session=MyUtil.getSqlSession(); Map map=new HashMap(); map.put("name", name); map.put("address", address); List<Emp> emps=session.selectList("askEmp2", map); return emps; }
(2)、 关键字元素: trim where set
-
trim元素
-
可以在自己包含的内容前面加上某些前缀,也可以在其后加上某些后缀,与之对应属性prefix和suffix
-
可以把包含内容的某些首部过滤掉也就是忽略,也可以把包含内容的某些尾部过滤掉,对应设置属性prefixOverrides和suffix Overrides
<!-- trim元素的where应用 --> <select id="askEmp3" parameterType="map" resultType="com.qst.pojo.Emp"> select * from t_emp <trim prefix="where" prefixOverrides="and|or"> <if test="name!=null and name!=''"> and name=#{name} </if> <!-- ps:使用and连接 --> <if test="address!=null and address!=''"> and address=#{address} </if> </trim> </select>
//trim元素应用,dao里面的方法 public List<Emp> askEmp3(String name,String address){ SqlSession session=MyUtil.getSqlSession(); Map map=new HashMap(); map.put("name", name); map.put("address", address); List<Emp> emps=session.selectList("askEmp3", map); return emps; }
<!-- trim元素的set应用 --> <update id="askEmp_set" parameterType="com.qst.pojo.Emp" > update t_emp <trim prefix="set" suffixOverrides=","> <if test="name!=null and name!=''"> name=#{name}, </if> <if test="address!=null and address!=''"> address=#{address} </if> </trim> where id=#{id} </update>
//trim元素的set应用 public void changeEmp(Emp emp){ SqlSession session=MyUtil.getSqlSession(); session.update("askEmp_set", emp); session.commit(); session.close(); }
-
-
where元素
<!-- where元素应用 --> <select id="whereEmp" parameterType="map" resultType="com.qst.pojo.Emp"> select * from t_emp <where> <if test="name!=null and name!=''"> name=#{name} </if> <if test="address!=null and address!=''"> and address=#{address} </if> </where> </select>
//where元素的应用,dao里面的方法 public List<Emp> whereEmp(String name,String address){ SqlSession session=MyUtil.getSqlSession(); Map map=new HashMap(); map.put("name", name); map.put("address", address); List<Emp> emps=session.selectList("whereEmp", map); return emps; }
-
set元素
<!-- set元素的应用 --> <update id="setEmp" parameterType="com.qst.pojo.Emp"> update t_emp <set> <if test="name!=null and name!=''"> name=#{name}, </if> <if test="address!=null and address!=''"> address=#{address} </if> </set> where id=#{id} </update>
//set元素的应用 public void changeEmp2(Emp emp){ SqlSession session=MyUtil.getSqlSession(); session.update("setEmp", emp); session.commit(); session.close(); }
(3)、循环元素:foreach
-
foreach实现了循环逻辑,可以进行一个集合迭代,主要用于构建in条件中
<!-- foreach元素的使用,批量查询 --> <select id="foreachEmp" parameterType="com.qst.pojo.Emp" resultType="com.qst.pojo.Emp"> select * from t_emp where id in <foreach collection="arr" item="arr" open="(" separator="," close=")"> #{arr} </foreach> </select> <!-- foreach元素的使用,批量删除 --> <delete id="forEmpDel" parameterType="com.qst.pojo.Emp" > delete from t_emp where id in <foreach collection="arr" item="arr" open="(" separator="," close=")"> #{arr} </foreach> </delete>
//foreach批量查询 public List<Emp> foreachEmp(int[] arrs) { SqlSession session=MyUtil.getSqlSession(); Emp e=new Emp(); e.setArr(arrs); List<Emp> emp=session.selectList("foreachEmp",e); session.close(); return emp; } //foreach批量删除 public void forEmpDel(int[] arrs){ SqlSession session=MyUtil.getSqlSession(); Emp e=new Emp(); e.setArr(arrs); session.delete("forEmpDel",e); session.commit(); System.out.println("删除成功"); session.close(); } public static void main(String[] args) { Emp emp = new Emp(); EmpDao dao = new EmpDao(); //foreach批量查询 int arr[]={2,4,7}; List<Emp> emps=dao.foreachEmp(arr); System.out.println(emps);*/ //foreach批量删除 int arrs[]={2,16,17}; dao.forEmpDel(arrs); }
四、Mybatis框架的关联映射
1.介绍
- 在查询时经常需要两个或者两个以上关联表的数据,通过关联映射可以由一个对象获取关联对象的数据。例如查询一个emp对象,可以通过关联映射获取员工所在部门的dept对象信息
2.实现形式
(1)、嵌套查询:
- 通过执行另外的一个SQL语句来返回关联数据结果(查两次)
<!-- 关联映射之多对一应用嵌套查询 -->
<select id="joinEmp" parameterType="int" resultMap="empJoin">
select * from t_emp where id=#{id}
</select>
<resultMap type="com.qst.pojo.Emp" id="empJoin">
<!-- property:关联属性名在Emp类里面的dept javaType:关联的类型 column:关联的数据库字段,外键-->
<association property="dept" javaType="com.qst.pojo.Dept" column="dept_id" select="askDeptId">
</association>
</resultMap>
<select id="askDeptId" parameterType="int" resultType="com.qst.pojo.Dept">
select * from t_dept where id=#{dept_id}
</select>
//多对一关联映射嵌套查询
public Emp findEmpJoinById(int id){
SqlSession session=MyUtil.getSqlSession();
Emp emp=session.selectOne("joinEmp", id);
return emp;
}
public static void main(String[] args) {
Emp emp = new Emp();
EmpDao dao = new EmpDao();
//关联映射嵌套查询
emp=dao.findEmpJoinById(15);
System.out.println(emp);
}
(2)、嵌套结果查询:
<!-- 关联映射多对一应用之嵌套结果 -->
<select id="joinEmp2" parameterType="int" resultMap="empJoin2">
select e.id,e.name,e.sex,e.salary,e.address,d.id as d_id,d.name as d_name
from t_emp as e join t_dept as d on e.dept_id=d.id
where e.id=#{id}
</select>
<resultMap type="com.qst.pojo.Emp" id="empJoin2">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="sex" column="sex"/>
<result property="salary" column="salary"/>
<result property="address" column="address"/>
<association property="dept" javaType="com.qst.pojo.Dept" column="dept_id">
<id property="id" column="d_id"/>
<result property="name" column="d_name" />
</association>
</resultMap>
//多对一关联映射嵌套结果
public Emp findEmpJoinById2(int id){
SqlSession session=MyUtil.getSqlSession();
Emp emp=session.selectOne("joinEmp2", id);
return emp;
}
public static void main(String[] args) {
Emp emp = new Emp();
EmpDao dao = new EmpDao();
//关联映射嵌套查询
emp=dao.findEmpJoinById2(1);
System.out.println(emp);
}
(3)、使用实体类存储:
<!-- 关联映射多对一应用之使用实体类存储 ps:记得取别名,当两张表重名时!-->
<select id="joinEmp3" parameterType="int" resultType="com.qst.pojo.Ed">
select e.id ,e.name,e.sex,e.salary,e.address,d.name as deptName
from t_emp e,t_dept d where e.dept_id=d.id and e.id=#{id}
</select>
//多对一关联映射pojo存储
public Ed findEmpJoinById3(int id){
SqlSession session=MyUtil.getSqlSession();
Ed ed=session.selectOne("joinEmp3", id);
return ed;
}
public static void main(String[] args) {
Ed ed = new Ed();
EmpDao dao = new EmpDao();
//关联映射嵌套查询
ed=dao.findEmpJoinById2(1);
System.out.println(ed);
}
package com.qst.pojo;
//创建一个实体类来存储
public class Ed {
private Integer id;
private String name;
private String sex;
private Double salary;
private String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
private String deptName;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Double getSalary() {
return salary;
}
public void setSalary(Double salary) {
this.salary = salary;
}
public String getDeptName() {
return deptName;
}
public void setDeptName(String deptName) {
this.deptName = deptName;
}
@Override
public String toString() {
return "Ed [id=" + id + ", name=" + name + ", sex=" + sex + ", salary="
+ salary + ", address=" + address + ", deptName=" + deptName
+ "]";
}
}
五、集合映射
1、介绍
- 当查询某个表的记录信息时。如果关联表有多条相关记录,此时就可以使用集合映射,例如:查询某个部门的对象信息,通过集合映射可以获取该部门中的所有员工对象信息
2、实现方式
(1)、嵌套查询
private List<Emp> emps;//首先在Dept类中添加集合类型
<!-- 一对多关系映射之嵌套查询 -->
<select id="askDept" parameterType="int" resultMap="deptMap">
select * from t_dept where id=#{id}
</select>
<resultMap type="com.qst.pojo.Dept" id="deptMap">
<id property="id" column="id" />
<result property="name" column="name" />
<collection property="emps" column="id" javaType="java.util.List"
ofType="com.qst.pojo.Emp" select="findEmpBy">
</collection>
</resultMap>
<select id="findEmpBy" parameterType="int" resultType="com.qst.pojo.Emp">
select * from t_emp where dept_id=#{id}
</select>
//一对多嵌套查询
public Dept findDeptById(int id) {
SqlSession session = MyUtil.getSqlSession();
Dept dept = session.selectOne("askDept", id);
session.close();
return dept;
}
(2)、嵌套结果
<select id="askDept2" parameterType="int" resultMap="deptMap1">
select d.id,d.name,e.id as e_id,e.name as e_name,e.sex,e.salary,e.address
from t_dept d,t_emp e where d.id=e.dept_id and d.id=#{id}
</select>
<resultMap type="com.qst.pojo.Dept" id="deptMap1">
<id property="id" column="id" />
<result property="name" column="name" />
<collection property="emps" javaType="java.util.List"
ofType="com.qst.pojo.Emp">
<id property="id" column="e_id" />
<result property="name" column="e_name" />
<result property="sex" column="sex" />
<result property="salary" column="salary" />
<result property="address" column="address" />
</collection>
</resultMap>
//一对多嵌套结果
public Dept findDeptById2(int id) {
SqlSession session = MyUtil.getSqlSession();
Dept dept = session.selectOne("askDept2", id);
session.close();
return dept;
}
public static void main(String[] args) {
DeptDao dd=new DeptDao();
Dept dept=new Dept();
dept=dd.findDeptById2(2);
System.out.println(dept);
}
六、映射器
1、介绍
- 映射器是MyBatis中最核心的组件之一,而从MyBatis 3开始,还支持接口映射非常简洁;映射器主要是在MyBatis和Spring框架整合集成时使用。
2.实现
(1)、创建接口
public interface EmpMapper {
public List<Emp> findAll();//该方法名和mapper.xml的id名称必须一致
}
(2)、获取映射对象
public class MapperFactory {
public static EmpMapper getEmpMapper(){
SqlSession session=MyUtil.getSqlSession();
//获取映射器对象
EmpMapper mapper=session.getMapper(EmpMapper.class);
return mapper;
}
}
(3)、测试
ps:进行测试之前,要记得把namespace里的路径改成你创建的接口路径
<mapper namespace="com.qst.dao.EmpMapper">
public class Test1 {
public static void main(String[] args) {
EmpMapper em=MapperFactory.getEmpMapper();
List<Emp> emps=em.findAll();
System.out.println(emps);
}
}