一、MyBatis持久层框架
1、概念
MyBatis的前身就是iBatis,iBatis本是apache的一个开源项目,2010年5月这个项目由apahce sofeware foundation 迁移到了google code,并且改名为MyBatis。
MyBatis 是支持普通 SQL 查询,存储过程和高级映射的优秀持久层框架。MyBatis 消除了几乎所有的 JDBC 代码和参数的手工设置以及结果集的检索。
1)简化JDBC的开发
2)能够更好的完成ORM(对象关系映射)
3)核心配置文件 – 用来配置数据库的连接的参数
4)映射文件 – 用来写SQL
两个核心的工具类:
1)SqlSessionFactory – 会话工厂,用来产生会话
2)SqlSession – 会话,用来执行SQL
3)ORM:对象关系映射,是指把表里字段的值 自动映射给 类里的属性
2、内部组件结构图
二、入门案例
1、准备数据表—数据库
create database mybatisdb default character set utf8;
use mybatisdb;
create table user(id int primary key auto_increment,name varchar(100),addr varchar(100),age int);
Insert into user values(null,'hanmeimei','北京',28);
Insert into user values(null,'xiongda','上海',20);
Insert into user values(null,'xiaonger','上海',19);
DROP TABLE IF EXISTS `dept`;
CREATE TABLE `dept` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`dname` varchar(14) DEFAULT NULL,
`loc` varchar(13) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of dept
-- ----------------------------
INSERT INTO `dept` VALUES ('1', '呵呵呵', '一区');
INSERT INTO `dept` VALUES ('2', '哈哈哈哈', '二区');
INSERT INTO `dept` VALUES ('3', 'operations', '二区');
INSERT INTO `dept` VALUES ('5', 'java教研部', '大钟寺');
INSERT INTO `dept` VALUES ('10', '开发', '西二旗');
DROP TABLE IF EXISTS `emp`;
CREATE TABLE `emp` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`ename` varchar(10) DEFAULT NULL,
`job` varchar(9) DEFAULT NULL,
`mgr` decimal(4,0) DEFAULT NULL,
`hiredate` date DEFAULT NULL,
`sal` decimal(7,2) DEFAULT NULL,
`comm` decimal(7,2) DEFAULT NULL,
`deptno` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=510 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of emp
-- ----------------------------
INSERT INTO `emp` VALUES ('100', 'jack', '副总', null, '2002-05-03', '90000.00', null, '1');
INSERT INTO `emp` VALUES ('200', 'tony', '总监', '100', '2015-02-02', '10000.00', '2000.00', '2');
INSERT INTO `emp` VALUES ('300', 'hana', '经理', '200', '2017-02-02', '8000.00', '1000.00', '2');
INSERT INTO `emp` VALUES ('400', 'leo', '员工', '300', '2019-02-22', '3000.00', '200.12', '2');
INSERT INTO `emp` VALUES ('500', 'liu', '员工', '300', '2019-03-19', '3500.00', '200.58', '2');
INSERT INTO `emp` VALUES ('502', '王一博', 'topidol.', '1000', '2021-03-31', '20000.00', '99.00', '88');
INSERT INTO `emp` VALUES ('504', '蔡徐坤', 'rapper', '10', '2021-03-29', '100.00', '1000.00', '100');
2、Maven工程结构
=注:导入jUnit5,使用@BeforeEach注解替代jUnit4的@Before。
3、修改pom文件,添加mybatis的jar包依赖,pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<artifactId>cgb2105sboot01</artifactId>
<groupId>cn.tedu</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>mybatusdbday17</artifactId>
<dependencies>
<!--jdbc依赖包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.15</version>
</dependency>
<!--mybatis依赖包-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
</project>
4、创建User类,User.java
package cn.tedu.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
//完成ORM,把表里的字段的值 查到 封装给类里的属性
//lombok标签,用来自动创建get、set、tostring、构造方法等
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true) //开启链式编程
//Mybatis自动完成ORM,把表里的字段的值 查到 封装给 类里的属性
//要求:属性名要和字段名一样
public class User {
private Integer id;
private String name;
private String addr;
private String age;
}
4、创建核心配置文件,指定连接数据库的参数,mybatis-config.xml
步骤:new------file------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">
<!-- mybatis的核心配置文件 -->
<configuration>
<!--用来设置别名 type是类的全路径 alisa是以后要使用的别名-->
<typeAliases>
<typeAlias type="cn.tedu.pojo.User" alias="User"></typeAlias>
<typeAlias type="cn.tedu.pojo.Dept" alias="Dept"></typeAlias>
</typeAliases>
<!--可以配置多个数据库连接的环境,使用default指定默认用哪个-->
<environments default="test">
<!--配置了具体连接数据库的参数-->
<environment id="test">
<!--使用的事务管理器-->
<transactionManager type="JDBC"></transactionManager>
<!--数据源:就是制定了数据库连接时的参数-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatisdb?characterEncoding=utf8&serverTimezone=Asia/Shanghai" />
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!--引入映射文件-->
<mappers>
<mapper resource="UseMapper.xml"/>
<mapper resource="DeptMapper.xml"/>
</mappers>
</configuration>
5、创建映射文件,写SQL,UserMapper.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用来作为文件的唯一标识
-->
<mapper namespace="UserMapperNS">
<!--select标签表示要发起查询的SQL,
id是SQL语句的唯一标识,
resultType用来完成ORM,把表里的字段值 自动映射 类里的属性
-->
<!--查询user表中所有的信息-->
<select id="getAll" resultType="cn.tedu.pojo.User">
select * from user
</select>
<!--查询id=1的user信息-->
<select id="getById" resultType="cn.tedu.pojo.User">
select * from user where id = 1
</select>
<!--动态查询id=?的user信息
select* from user where id=1 不要把参数1写死,太死板了
${???}动态获取执行SQL时传入的参数
-->
<select id="getId" resultType="User">
select * from user where id = ${id}
</select>
<!--动态查询user信息-->
<select id="getName" resultType="User">
<!--
面试题:$ 和 #取值的区别?
都能获取参数的值,$只获取值本身不拼接单引号,#获取到值以后自动拼接单引号
$底层使用Statement不安全低效,#底层使用PreparedStatement高效安全SQL简单
select * from user where name = ${name}
-->
select * from user where name = #{name}
</select>
<!--动态查询user信息-->
<select id="getIdName" resultType="User">
select * from user where name = #{name} and id = #{id}
</select>
<!--查询上海有几个人-->
<!--
resultType是指查询的结果要交给谁处理,
结果可以是写一个类的路径,也可以写int string等
-->
<select id="getAddr" resultType="int">
select count(1) from user where addr = #{addr}
</select>
</mapper>
6、修改核心配置文件,加入映射文件
<!--引入映射文件-->
<mappers>
<mapper resource="UseMapper.xml"/>
<mapper resource="DeptMapper.xml"/>
</mappers>
7、创建单元测试类,Test.java
package cn.tedu;
import cn.tedu.pojo.Dept;
import cn.tedu.pojo.User;
import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;
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 org.junit.jupiter.api.Test;
import java.io.InputStream;
import java.util.List;
//测试
public class test {
@Test
public void get() throws Exception {
//1、读取核心配置文件---创建字节输入流对象用于读取
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
//2、创建会话工厂
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//3、创建会话
SqlSession session = factory.openSession();
//4、执行sql
//List<Object> getAll = session.selectList("namespace的值.id的值"); 唯一的定位的sql
List<User> list = session.selectList("UserMapperNS.getAll"); //查询出多个结果
//4、处理结果
for (User u: list) {
System.out.println(u);
}
User user = session.selectOne("UserMapperNS.getById");//查询出一个结果
System.out.println(user);
//动态查询id=2的信息
User user1 = session.selectOne("UserMapperNS.getId",2);//查询出一个结果
System.out.println(user1);
//动态查询name=xiaonger的信息
//selectOne(1,2)--1是SOL定位的方式,2是soL需要的参数
User user2 = session.selectOne("UserMapperNS.getName", "xiaonger");//查询出一个结果
System.out.println(user2);
//动态查询name=xiaonger的信息
//selectlist(1,2)--1是SOL定位的方式,2是soL需要的参数
List<User> list1 = session.selectList("UserMapperNS.getName", "xiaonger");//查询出一个结果
for (User u: list1) {
System.out.println(u);
}
int count = session.selectOne("UserMapperNS.getAddr", "上海");
System.out.println(count);
}
}
8、测试
三、参数解析
1、别名:alias
在sqlMapConfig.xml配置,在映射文件中直接写对象名称即可
设置别名:修改核心配置文件,加一个typeAliases标签
<!--用来设置别名 type是类的全路径 alisa是以后要使用的别名-->
<typeAliases>
<typeAlias type="cn.tedu.pojo.User" alias="User"></typeAlias>
<typeAlias type="cn.tedu.pojo.Dept" alias="Dept"></typeAlias>
</typeAliases>
使用别名:在映射文件里,在resultType处直接使用别名
<select id="getAll" resultType="User">
select * from user
</select>
2、参数值:paramterType
指定参数类型,通常制定一个对象类型。
3、返回值:resultType
非常重要的东西,即完成ORM的映射关系所在。
这里将指定的cd.tedu.mybatis.domain.User代表 把 结果集 转换成一个User 对象实例。
4、返回值:resultMap
resultMap 用于对复杂对象结构时,对应的ResultMap结构名称
5、#和$的区别
两种方式都可以获取参数的值。区别如下:
(推荐!)#:
使用#{parameterName}引用参数的时候,Mybatis会把这个参数认为是一个字符串,例如传入参数是"Smith",那么在SQL(Select * from emp where name = #{employeeName})使用的时候就会转换为Select * from emp where name = ‘Smith’。
$:
不做字符串拼接,SQL(Select * from emp where name = ${employeeName})使用的时候就会转换为Select * from emp where name = Smith。此时,如果字段是varchar类型直接抛出SQL异常。
总结:
都能获取参数的值,
$只获取值本身不拼接单引号
#获取到值以后自动拼接单引号
$底层使用Statement不安全低效
#底层使用PreparedStatement高效安全SQL简单
6、xml的转义符号
在xml文件中,一些符号比较特殊,需要转译
xml文档中,定义了一些实体,以&开始,以;结束
代码 | 符号 | 解释 |
---|---|---|
<; | < | 小于号 |
&git; | > | 大于号 |
&; | & | 和 |
&apos; | ’ | 单引号 |
" | " | 双引号 |
四、SQL中有特殊字符
当SQL中有特殊字符,mybatis不能正常解析时,
用<![CDATA[ ?? ]]>括起来就解决了 <![CDATA[ and age<=#{age} ]]>
<![CDATA[
and age<=#{age}
]]>
五、动态SQL
Mybatis提供使用ognl表达式动态生成sql的功能。
1、sql和include
Sql标签用来提取SQL片段,来提高SQL的复用.
使用位置需要通过include引用指定的SQL片段.
<!-- 提取sql片段 -->
<sql id="names">
id,dname,loc
</sql>
<!--查询dept表中所有的信息-->
<select id="getAll" resultType="Dept">
<!-- 引入标签前的代码:select * from dept-->
<!--引入后-->
/* include引入sql片段,refid指定sql片段的id值 */
select <include refid="names"></include>
from dept
</select>
2、if
执行SQL时,可以添加一些判断条件
<!--查询id=1的记录 if判断-->
<select id="getId1" resultType="Dept">
select <include refid="names"></include>
from dept
<!-- 使用if标签用来判断,条件成立才执行sql,不成就不执行-->
<if test="id != null">
where id = #{id}
</if>
</select>
3、where
去掉条件中可能多余的and或者or
<!--查询id=1或者3的部门信息 where 去除多余的and或者or-->
<select id="getIds" resultType="Dept">
select
<include refid="names"></include>
from dept
<!-- 使用if标签用来判断,条件成立才执行sql,不成就不执行-->
<where>
<if test=" id != null ">
id =#{id}
</if>
or id =3
</where>
</select>
4、set
去掉最后可能多余的逗号:
<update id="update">
UPDATE teachers
<set>
<if test="tname != null">tname=#{tname},</if>
<if test="tsex != null">tsex=#{tsex},</if>
<if test="tbirthday != null">tbirthday=#{tbirthday},</if>
<if test="prof != null">prof=#{prof},</if>
<if test="depart != null">depart=#{depart}</if>
</set>
WHERE tno=#{tno}
</update>
5、foreach
用于in子查询中的多个值的遍历:
<!--删除id 1,2,3的数据-->
<select id="delId1">
<!--delete from dept where id in(1,2,3),参数写死了,最好动态解析-->
delete from dept where id in
<!--
foreach用来遍历,collection属性的值是固定值array list map里的key
item相当于遍历得到的数据 separator是数据间的分隔符
open是开始,close是结束
-->
<foreach collection="array" item="i" separator="," open="(" close=")">
#{i}
</foreach>
</select>
调用代码:
//id=1或者3的部门信息
List<Dept> list7 = session.selectList("DeptMapperNS.getIds",19);
for (Dept d : list7) {
System.out.println(d);
}
//删除id 1,2,3的数据
Integer[] arr = {1,2,3};
session.delete("DeptMapperNS.delId1",arr);
//
6、案例
6.1、添加依赖,pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<artifactId>cgb2105sboot01</artifactId>
<groupId>cn.tedu</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>mybatisdbday18</artifactId>
<dependencies>
<!--lombok依赖包-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--jdbc依赖包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.15</version>
</dependency>
<!--mybatis依赖包-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
</dependencies>
</project>
6.2、Dept类,Dept.java
package cn.tedu.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
//Mybatis自动完成ORM,把表里的字段的值 查到 封装给 类
//要求:属性名要和字段名一样
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class Dept {
private Integer id;
private String dname;
private String loc;
}
6.3、核心配置文件,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">
<!-- mybatis的核心配置文件 -->
<configuration>
<!--用来设置别名 type是类的全路径 alisa是以后要使用的别名-->
<typeAliases>
<typeAlias type="cn.tedu.pojo.Dept" alias="Dept"></typeAlias>
</typeAliases>
<!--可以配置多个数据库连接的环境,使用default指定默认用哪个-->
<environments default="test">
<!--配置了具体连接数据库的参数-->
<environment id="test">
<!--使用的事务管理器-->
<transactionManager type="JDBC"></transactionManager>
<!--数据源:就是制定了数据库连接时的参数-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatisdb?characterEncoding=utf8&serverTimezone=Asia/Shanghai" />
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!--引入映射文件-->
<mappers>
<mapper resource="DeptMapper.xml"/>
</mappers>
</configuration>
6.4、DeptMapper.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用来作为文件的唯一标识
-->
<mapper namespace="cn.tedu.dao.DeptDao">
<!--select标签表示要发起查询的SQL,
id是SQL语句的唯一标识,
resultType用来完成ORM,把表里的字段值 自动映射 类里的属性
-->
<!-- 提取sql片段 -->
<sql id="names">
id,dname,loc
</sql>
<!--静态查询dept表中所有的信息-->
<!--resultType使用了别名,因为在核心配置文件里配过了 -->
<select id="getAll1" resultType="Dept">
select * from dept;
</select>
<!--动态查询dept表中所有的信息-->
<select id="getAll" resultType="Dept">
<!-- 引入标签前的代码:select * from dept-->
<!--引入后-->
/* include引入sql片段,refid指定sql片段的id值 */
select
<include refid="names"></include>
from dept
</select>
<!--查询name=java教研部 的部门表部门信息 -->
<select id="getName" resultType="Dept">
select
<include refid="names"></include>
from dept
where
dname = #{dname}
</select>
<!--查询name=java教研部 的部门表部门信息 if判断-->
<select id="getName1" resultType="Dept">
select
<include refid="names"></include>
from dept
<!-- 使用if标签用来判断,条件成立才执行sql,不成就不执行-->
<if test=" dname != null ">
where dname= #{dname}
</if>
</select>
<!--查询id=1或者3的部门信息 where 去除多余的and或者or-->
<select id="getIds" resultType="Dept">
select
<include refid="names"></include>
from dept
<!-- 使用if标签用来判断,条件成立才执行sql,不成就不执行-->
<where>
<if test=" id != null ">
id =#{id}
</if>
or id =3
</where>
</select>
<!--删除id 1,2,3的数据-->
<select id="delId1">
<!--delete from dept where id in(1,2,3),参数写死了,最好动态解析-->
delete from dept where id in
<!--
foreach用来遍历,collection属性的值是固定值array list map里的key
item相当于遍历得到的数据 separator是数据间的分隔符
open是开始,close是结束
-->
<foreach collection="array" item="i" separator="," open="(" close=")">
#{i}
</foreach>
</select>
</mapper>
6.5、单元测试
package cn.tedu;
import cn.tedu.pojo.Dept;
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 org.junit.jupiter.api.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class Test1 {
@Test
public void get() throws IOException {
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//开启会话,给参数true,自动提交事务
SqlSession session = factory.openSession(true);//true:自动提交事务,默认是false
//执行SQL
List<Dept> list = session.selectList("DeptMapperNS.getAll");
for (Dept d : list) {
System.out.println(d);
}
//Mybatis的以及缓存:相同的SqlSession执行相同的SQL时会查缓存不会再次发起SQL了
List<Dept> list2 = session.selectList("DeptMapperNS.getAll");
for (Dept d : list2) {
System.out.println(d);
}
//引入sql片段查询
List<Dept> list3 = session.selectList("DeptMapperNS.getAll1");
for (Dept d : list3) {
System.out.println(d);
}
//查询name=java教研部 的部门表部门信息
List<Dept> list4 =session.selectList("DeptMapperNS.getName","java教研部");
for (Dept d : list4) {
System.out.println(d);
}
//查询name=java教研部 的部门表部门信息 if判断
List<Dept> list5 = session.selectList("DeptMapperNS.getName","java教研部");
for (Dept d : list5) {
System.out.println(d);
}
//查询null 的部门表部门信息 if判断
List<Dept> list6 = session.selectList("DeptMapperNS.getName1",null);
for (Dept d : list6) {
System.out.println(d);
}
//id=1或者3的部门信息
List<Dept> list7 = session.selectList("DeptMapperNS.getIds",19);
for (Dept d : list7) {
System.out.println(d);
}
//删除id 1,2,3的数据
Integer[] arr = {1,2,3};
session.delete("DeptMapperNS.delId1",arr);
//
/*开启会话,给参数true,自动提交事务
SqlSession session = factory.openSession(true);
session.commit();//增删改都需要提交事务,不然对数据库没有持久性的影响---就可以省略
*/
System.out.println("删掉了");
}
}
==Mybatis的以及缓存:相同的SqlSession执行相同的SQL时会查缓存不会再次发起SQL
增删改都需要提交事务,不然对数据库没有持久性的影响
Mybatis不会自动提交事务,需要手动提交:openSession(true)或者commit() ==
六、MyBatis:接口映射方式
1、概述
在上面的测试用例中,在调用session的方法的时候,都会传入要调用的SQL的namespace+id名称,这不是必须的。可以只传入id即可。但是,如果在mybatis的环境中有多个相同id的映射名称,就会报错。所以,一般情况下,调用方法最好还是使用namespace+id。但是,namespace+id的使用方式很容易报错,因为是string类型的,没有检查。所以,mybatis提供了一种非常好的设计方式来避免这种问题,即Mapper接口。
2、案例
2.1)maven工程结构
2.2)修改映射文件,把namespace的值改成接口的全路径,DeptMapper.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用来作为文件的唯一标识,类的全路径
-->
<mapper namespace="cn.tedu.dao.DeptDao">
<!--select标签表示要发起查询的SQL,
id是SQL语句的唯一标识,
resultType用来完成ORM,把表里的字段值 自动映射 类里的属性
-->
<!-- 提取sql片段 -->
<sql id="names">
id,dname,loc
</sql>
<!--静态查询dept表中所有的信息-->
<!--resultType使用了别名,因为在核心配置文件里配过了 -->
<select id="getAll1" resultType="Dept">
select * from dept;
</select>
<!--动态查询dept表中所有的信息-->
<select id="getAll" resultType="Dept">
<!-- 引入标签前的代码:select * from dept-->
<!--引入后-->
/* include引入sql片段,refid指定sql片段的id值 */
select
<include refid="names"></include>
from dept
</select>
<!--查询name=java教研部 的部门表部门信息 -->
<select id="getName" resultType="Dept">
select
<include refid="names"></include>
from dept
where
dname = #{dname}
</select>
<!--查询name=java教研部 的部门表部门信息 if判断-->
<select id="getName1" resultType="Dept">
select
<include refid="names"></include>
from dept
<!-- 使用if标签用来判断,条件成立才执行sql,不成就不执行-->
<if test=" dname != null ">
where dname= #{dname}
</if>
</select>
<!--查询id=1或者3的部门信息 where 去除多余的and或者or-->
<select id="getIds" resultType="Dept">
select
<include refid="names"></include>
from dept
<!-- 使用if标签用来判断,条件成立才执行sql,不成就不执行-->
<where>
<if test=" id != null ">
id =#{id}
</if>
or id =3
</where>
</select>
<!--删除id 1,2,3的数据-->
<select id="delId1">
<!--delete from dept where id in(1,2,3),参数写死了,最好动态解析-->
delete from dept where id in
<!--
foreach用来遍历,collection属性的值是固定值array list map里的key
item相当于遍历得到的数据 separator是数据间的分隔符
open是开始,close是结束
-->
<foreach collection="array" item="i" separator="," open="(" close=")">
#{i}
</foreach>
</select>
</mapper>
2.3)修改核心配置文件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">
<!-- mybatis的核心配置文件 -->
<configuration>
<!--用来设置别名 type是类的全路径 alisa是以后要使用的别名-->
<typeAliases>
<typeAlias type="cn.tedu.pojo.Dept" alias="Dept"></typeAlias>
</typeAliases>
<!--可以配置多个数据库连接的环境,使用default指定默认用哪个-->
<environments default="test">
<!--配置了具体连接数据库的参数-->
<environment id="test">
<!--使用的事务管理器-->
<transactionManager type="JDBC"></transactionManager>
<!--数据源:就是制定了数据库连接时的参数-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatisdb?characterEncoding=utf8&serverTimezone=Asia/Shanghai" />
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!--引入映射文件-->
<mappers>
<mapper resource="DeptMapper.xml"/>
</mappers>
</configuration>
2.4)创建接口文件,DeptDao.java
接口文件的全路径 = Mapper.xml里namespace的值
接口中的方法名 = Mapper.xml里的SQL的id值
接口中的方法的返回值 = Mapper.xml里的SQL的resultType值
package cn.tedu.dao;
import cn.tedu.pojo.Dept;
import java.util.List;
public interface DeptDao {
/**
* 获取所有
* @return 所有部门信息
*/
List<Dept> getAll();
/**
* 根据名称获取部门信息
* @return 相关名字的部门信息
*/
List<Dept> getName(String name);
List<Dept> getByIds(int[] id);
void delId1(int[] id);
}
2.5)创建单元测试类,测试
package cn.tedu;
import cn.tedu.dao.DeptDao;
import cn.tedu.pojo.Dept;
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 org.junit.jupiter.api.Test;
import java.io.InputStream;
import java.util.List;
public class Test2 {
@Test
public void get() throws Exception{
//加载核心配置文件
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
//创建会话工厂
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//开启会话,给参数true,自动提交事务
SqlSession session = factory.openSession(true);//true:自动提交事务,默认是false
//获取指定接口
DeptDao dao = session.getMapper(DeptDao.class);
//调用接口里的方法
List<Dept> list = dao.getAll();
for (Dept d : list) {
System.out.println(d);
}
//根据名称获取部门信息
List<Dept> list2 = dao.getName("开发");
for (Dept dept : list2) {
System.out.println(dept);
}
}
}
2.6)总结
七、ResultMap简单使用
1、概述
当数据库的字段名和对象的属性名 一致时,可以用简单属性resultType。
但是当数据库中的字段名称和对象中的属性名称 不 一致时,就需要resultMap属性。
2、案例
<?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="cn.tedu.mybatis.pojo.PersonMapper">
<!-- 最强大对象resultMap,结果封装到哪个pojo对象,type就是谁 -->
<resultMap type="Person" id="personRM">
<!—单独处理 属性名 和 字段名不一致的 -->
<result property="userName" column="user_name"/>
</resultMap>
<!-- 查询所有 -->
<select id="find" resultMap="personRM">
SELECT id,user_name FROM person WHERE id=#{id}
</select>
</mapper>
八、自动匹配规范驼峰规则
数据库中我们习惯使用全大写,多个单词用下划线隔开,而po对象中,习惯使用java驼峰规则。那一个一个手工写resultMap字段,浪费开发时间,直接配置一下就可以了。
如:
数据库字段: is_man
Javabean属性: private Integer isMan
mapper配置不需要写字段与属性的配置,会自动映射
注意:主键需要单独写,其它字段就可以直接利用驼峰规则自动映射。
第一步:在sqlMapConfig.xml中配置settings:
<?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>
<settings>
<!--开启驼峰规则-->
<setting name="mapUnderscoreToCamelCase" value="true" />
</settings>
</configuration>
第二步:resultMap配置autoMapping=“true”
<resultMap type="cn.tedu.jk.domain.Contract" id="contractRM" autoMapping="true">
<!-- <id property="id" column="CONTRACT_ID"/> -- >
</resultMap>
八、扩展
1、JDBC和MyBatis的区别?
JDBC是java提供了一套专门用于和数据库对接的api,java.sql.*,其规范了如何和数据库进行对接,实现由各数据库厂商进行各自的实现和扩展。学习JDBC重点在学习如何使用其api。
MyBatis框架是轻量级封装了JDBC,我们已经看不到这些api,连接connection、语句preparedstatement、结果集ResultSet,而关注的是mybatis框架体系如何去使用,一旦写好,我们关注的是java对象。
2、XML和接口方式的区别?
MyBatis提供了两种操作数据库的方式,一种是通过xml映射文件,一种是通过java的接口类。按面向对象方式更加推荐接口方式,但如果复杂的多表映射,仍然需要使用xml映射文件的ResultMap方式实现。
接口只是假象,其底层仍然是通过xml实现,好不容易实现了一套方式,怎忍丢掉呢?可以做个测试就知道它底层怎么实现的?把xml中的sql删除,它就玩不转了。
3、接口方式怎么找到xml执行的?
SqlSession的getMapper方法找到类,通过反射可以获取到类的全路径(包名.类名),相加后就定位到某个xml的命名空间namespace,在根据调用的方法去找到xml中某个标签的id属性。从而实现价值接口,调用接口的方法而间接找到xml中的标签,通过解析xml获取这个标签的内容,从而获取到sql语句。