1.MyBatis概念介绍
MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。
IBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层(Dao 数据访问层)框架。iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAO)
SSH Struts2+Spring+Hibernate(持久层框架)
SSM SpringMvc+Spring+MyBatis(持久层框架) 主流框架
Hibernate 全自动框架,SQL语句可以自动生成,不用人工书写!灵活度差,性能差,笨重。SQL定制麻烦! MyBatis 半自动框架,SQL语句还是需要自己书写!灵活!定制SQL!轻便! MyBatis Plus 插件:自动生成SQL、代码生成器!
ORM
ORM概念: Object Ralation Mapping 对象关系映射框架
数据库类和程序中的实体类有对应关系(映射关系)的框架,叫做ORM框架(对象关系映射)
• 数据库表 tb_user ------>实体类 User
• 数据库表中字段 username----->实体类的属性 username
• 数据库表中字段的类型 varchar(20) ----->实体类中属性的类型 String
数据库表和程序实体类有对应关系的持久层框架就叫ORM框架!
2.MyBatis特点
-
简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。
-
灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。 sql写在xml里,便于统一管理和优化。通过sql基本上可以实现我们不使用数据访问框架可以实现的所有功能,或许更多。
-
插件丰富,开源框架,有很多第三方插件(MyBatis Plus),辅助开发,甚至MyBatis自定义插件!
3.MyBatis快速入门
3.1 搭建MyBatis环境
3.1.1 环境准备
-
Jdk环境:jdk1.8
-
Ide环境:IDEA
-
数据库环境:MySQL 5.5.X
-
Mybatis:3.4.5
-
maven:3.6.X
3.1.2 导入MyBatis依赖
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.bruceliu.mybatis</groupId>
<artifactId>mybatis-20190902</artifactId>
<version>1.0-SNAPSHOT</version>
<!--导入MyBatis开发环境的依赖-->
<dependencies>
<!-- myBatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<!-- mysql驱动包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.25</version>
</dependency>
<!-- Junit测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--Lombok依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.18</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
3.1.3 准备数据库
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for `user`
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(32) NOT NULL COMMENT '用户名称',
`birthday` date DEFAULT NULL COMMENT '生日',
`sex` char(1) DEFAULT NULL COMMENT '性别',
`address` varchar(256) DEFAULT NULL COMMENT '地址',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', '王五', '2018-09-06', '1', '四川成都');
INSERT INTO `user` VALUES ('10', '张三', '2014-07-10', '1', '北京市');
INSERT INTO `user` VALUES ('16', '张小明', '2018-09-06', '1', '河南郑州');
INSERT INTO `user` VALUES ('22', '陈小明', '2018-09-05', '1', '河南郑州');
INSERT INTO `user` VALUES ('24', '张三丰', '2018-09-13', '1', '河南郑州');
INSERT INTO `user` VALUES ('25', '陈小明', '2018-09-12', '1', '河南郑州');
INSERT INTO `user` VALUES ('26', '王五', '2018-09-05', '0', '河南郑州');
3.1.4.创建主配置文件: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"/>
<!-- 环境 -->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<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>
<!--映射Mapper文件-->
<!--引入映射文件-->
<mappers>
<mapper resource="com/bruceliu/mapper/UserMappper.xml"></mapper>
</mappers>
</configuration>
jdbc.properties:
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=123456
3.2 实现MyBatis的查询
3.2.1 获取SqlSession对象(核心对象)
MyBatis框架中涉及到的几个API
SqlSessionFactoryBuilder:该对象负责根据MyBatis配置文件mybatis-config.xml构建SqlSessionFactory实例来生产session。
SqlSessionFactory:每一个MyBatis的应用程序都以一个SqlSessionFactory对象为核心。该对象负责创建SqlSession对象实例。
SqlSession:该对象包含了所有执行SQL操作的方法,用于执行已映射的SQL语句
3.2.2 创建实体类 User
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Users {
private Integer id;
private String username;
private String birthday;
private String sex;
private String address;
}
3.2.3 创建接口层 UserMapper
public interface UsersMapper {
List<Users> findALL();
}
3.2.4 创建接口实现层 UserMapperImpl
public class UsersMapperImpl implements UsersMapper {
public List<Users> findALL() {
InputStream inputStream = null;
SqlSessionFactory sqlSessionFactory = null;
SqlSession session = null;
try {
String resource = "mybatis-config.xml"; // 配置文件
inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
session = sqlSessionFactory.openSession(); //获取session
List<Users> list = session.selectList("com.it.mapper.UsersMapper.findALL");
return list;
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
session.close();
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
}
3.2.5 创建接口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.it.mappper.UsersMapper">
<!-- 返回值类型 -->
<select id="getList" resultType="com.it.pojo.User">
select * from user
</select>
</mapper>
3.2.6 测试类
@Test
public void test01(){
UsersMapper usersMapper = new UsersMapperImpl();
List<Users> users = usersMapper.findALL();
for (Users user : users){
System.out.println(user);
}
}
3.2.6.Mybatis使用步骤总结:
1) 创建SqlSessionFactory
2) 通过SqlSessionFactory创建SqlSession对象
3) 通过SqlSession操作数据库
4) 调用session.commit()提交事务
5) 调用session.close()关闭会话
3.3 MyBatis开启SQL日志打印
在MyBatis的配置文件中设置setting标签
3.4封装获取MyBatis中Session的工具类
public class MyBatisUtil {
private static ThreadLocal<SqlSession> threadLocal = new ThreadLocal<SqlSession>();
private static SqlSessionFactory sqlSessionFactory;
/**
* 静态代码块一次加载位于src/mybatis.xml配置文件
*/
static {
try {
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
/**
* 获取SqlSession
*/
public static SqlSession getSqlSession() {
//从当前线程中获取SqlSession对象
SqlSession sqlSession = threadLocal.get();
//如果SqlSession对象为空
if (sqlSession == null) {
//在SqlSessionFactory空的情况下,获取SqlSession对象
sqlSession = sqlSessionFactory.openSession();
//将SqlSession对象与当前线程绑定在一起
threadLocal.set(sqlSession);
}
//返回SqlSession对象
return sqlSession;
}
/**
* 关闭SqlSession与当前线程分开
*/
public static void closeSqlSession() {
//从当前线程中获取SqlSession对象
SqlSession sqlSession = threadLocal.get();
//如果SqlSession对象非空
if (sqlSession != null) {
//关闭SqlSession对象
sqlSession.close();
//分开当前线程与SqlSession对象的关系,目的是让GC尽早回收
threadLocal.remove();
}
}
}
4.编写DAO的实现类发现的问题
-
冗余的代码多;
-
调用Statement不方便;
-
通用性不好,实现方法非常的类似;
4.1 使用动态代理实现接口的实现类(不需要Mapper实现类)
如何得到动态代理:
public class TestMybatis {
SqlSession sqlSession=null;
UsersMapper usersMapper = null;
//在每次调用测试方法之前,自动调用init()方法
@Before
public void init(){
//MyBatis在底层使用动态代理(反射)自动生成Mapper实现类,不需要人工写实现类!
sqlSession = MyBatisUtil.getSqlSession();
//usersMapper就是Mapper的实现类
usersMapper = sqlSession.getMapper(UsersMapper.class);
}
@Test
public void testQuery(){
List<Users> list = usersMapper.findALL();
for (Users user : list) {
System.out.println(user);
}
}
/*
* MyBatis增删改 需要手动提交事务
*/
@Test
public void testAdd(){
Users u=new Users(null, "刘老师","2022-2-12 14:34:34","女","日本东京");
int count = usersMapper.adduser(u);
System.out.println(count>0?"新增成功":"新增失败");
}
//每次调用测试方法之前,自动调用一下destory()
@After
public void destory(){
MyBatisUtil.closeSqlSession();
}
}
4.2 使用动态代理总结
使用mapper接口不用写接口实现类即可完成数据库操作,使用非常简单,也是官方所推荐的使用方法。
使用mapper接口的必须具备以几个条件:
1) Mapper的namespace必须和mapper接口的全路径一致。
2) Mapper接口的方法名必须和sql定义的id一致。
3) Mapper接口中方法的输入参数类型必须和sql定义的parameterType一致。
4) Mapper接口中方法的输出参数类型必须和sql定义的resultType一致。
5.Mybatis-Config配置
5.1 Properties
<!-- 引入外部配置文件 -->
<properties resource="jdbc.properties"/>
5.2.typeAliases(别名)
类型别名是为 Java 类型命名一个短的名字。 它只和 XML 配置有关, 只用来减少类完全限定名的多余部分。 自定义别名:
<!--实体类取别名-->
<typeAliases>
<!--直接给所有的实体类取别名。默认的实体类的别名就是类名(不区分小大写)
User实体类:User、user、USER
-->
<package name="com.it.pojo"/>
</typeAliases>
注意: 使用定义的别名是不区分大小写的,但一般按java规则去使用即可,即user或者User
5.3.mappers
mapper映射文件的引入有3种方式: 路径相对于资源目录跟路径:
<mappers>
<mapper resource="com/bruceliu/dao/UserMapper.xml" />
</mappers>
使用完整的文件路径:
<mappers>
<mapper class="com.bruceliu.dao.UserMapper"/>
</mappers>
注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录(src目录中的Java目录下)中,可直接配个扫描包,但是需要注意的是idea可能不会编译Java目录下的xml文件,需要在pom文件中添加配置:
<build>
<!--配置编译目录,把src/main/java下的配置文件和src/main/resources 都编译下-->
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
</resource>
</resources>
</build>
在mybatis文件中的mappers标签下面直接配个扫描包
<!--引入映射文件-->
<mappers>
<!-- <mapper resource="com/bruceliu/mapper/UserMappper.xml"></mapper>-->
<!--<mapper class="com.bruceliu.mapper.UserMappper"></mapper>-->
<!--直接映射包的名字,那么这个包下面所有的Mapper接口全部映射!-->
<package name="com.bruceliu.mapper"/>
</mappers>
6.Mapper XML 文件
Mapper映射文件是在实际开发过程中使用最多的,也是我们学习的重点。 Mapper文件中包含的元素有:
cache – 配置给定命名空间的缓存。
cache-ref – 从其他命名空间引用缓存配置。
resultMap – 映射复杂的结果对象。
sql – 可以重用的 SQL 块,也可以被其他语句引用。
insert – 映射插入语句
update – 映射更新语句
delete – 映射删除语句
select – 映射查询语句
6.1.CRUD
6.1.1.select
<select id="getById" parameterType="int" resultType="User">
select * from user where id=#{id}
</select>
select标签用来编写查询语句的Statement
id,必要属性,在当前的命名空间下不能重复。
resultType指定输出类型,
parameterType(不是必须)如果不指定,自动识别。
6.1.2. insert
<!-- 增删改返回的都是int值 不用写返回值 -->
<insert id="addUser">
INSERT INTO USER VALUES (null,#{username},#{birthday},#{sex},#{address})
</insert>
6.1.3.如何获得到自增id
①在insert标签中开启自增长映射
<!-- 增删改返回的都是int值 不用写返回值 -->
<insert id="addUser" parameterType="User" useGeneratedKeys="true" keyProperty="id">
INSERT INTO USER VALUES (null,#{username},#{birthday},#{sex},#{address})
</insert>
useGeneratedKeys:开启自增长映射
keyProperty:指定id所对应对象中的属性名
当执行完saveUser()方法后,其返回值依然是执行sql影响的行数,并不是要获取的自增ID!Mybatis会自动将返回的主键值赋值给对象User的属性id,因此你可以通过属性的get方法获得插入的主键值: System.out.println(User.getId());
②在插入操作完成之前或之后,可以配置<selectKey>标签获得生成的主键的值,获得插入之前还是之后的值,可以通过配置order属性来指定。 LAST_INSERT_ID:该函数是mysql的函数,获取自增主键的ID,它必须配合insert语句一起使用
6.1.4.update
<update id="updateUser" >
update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id}
</update>
6.1.5.删除
<delete id="deleteUser" parameterType="int">
delete from user where id=#{id}
</delete>
delete标签叫Statement id,必要属性,在当前的命名空间下不能重复。 parameterType (不是必须)如果不指定,自动识别。
7. MyBatis的sql语句中#和$区别(面试题)
在Mybatis的mapper中,sql语句中参数传递有2种方式,一种是#{}另一种是${},两者有着很大的区别:
-
# {} 实现的是sql语句的预处理参数,之后执行sql中用?号代替,使用时不需要关注数据类型,Mybatis自动实现数据类型的转换。并且可以防止SQL注入。
-
${} 实现是sql语句拼接,不做数据类型转换,需要自行判断数据类型。不能防止SQL注入。
<!--
根据id查询用户,User findById(int id)
select:配置查询语句
id:可以通过id找到执行的statement,statement唯一标识
parameterType:输入参数类型
resultType:输出结果类型
#{}:相当于占位符
#{id}:其中的id可以表示输入参数的名称,如果是简单类型名称可以任意
-->
<select id="getById" parameterType="int" resultType="User" >
select * from user where id=#{id}
</select>
<!--
根据用户名称来模糊查询用户信息列表;
${}:表示拼接sql语句
${value}:表示输入参数的名称,如果参数是简单类型,参数名称必须是value
-->
<select id="findByUsername" parameterType="java.lang.String"
resultType="User">
select * from user where username like '%${value}%'
</select>
两种模糊查询的区别
8.parameterType的传入参数
8.1 .HashMap传入参数
查询需求:
<!-- 分页查询:map传参 -->
<select id="selectUserByPage" resultType="User">
SELECT * FROM USER LIMIT #{offset},#{pagestart}, #{pagesize}
</select>
接口:
List<User> selectUserByPage(Map<String, Object> paramList);
测试:
@Test
public void testSelectUserByPage() {
pageindex = 1;
Map<String, Object> map = new HashMap<String, Object>();
map.put("offset", 0);
map.put("pagestart", (pageindex - 1)*pagesize);
map.put("pagesize", 5);
List<User> userList = mapper.selectUserByPage(map);
for (int i = 0; i < userList.size(); i++) {
System.out.println(userList.get(i));
}
}
注意:map的key要和sql中的占位符保持名字一致
8.2使用注解@Param(名字传入参数)
注意:mapper文件中的参数占位符的名字一定要和接口中参数的注解保持一致
查询需求:
<select id="selectUserByPage2" resultType="User">
SELECT * FROM USER LIMIT #{offset}, #{pagesize}
</select>
接口:
List<User> selectUserByPage2(@Param(value = "offset") int offset,
@Param(value = "pagesize") int pagesize);
测试:
@Test
public void testSelectUserByPage2() {
List<User> userList = mapper.selectUserByPage2(0, 2);
for (int i = 0; i < userList .size(); i++) {
System.out.println(userList .get(i));
}
}
8.3 使用参数顺序(位置传入参数)
注意:mapper文件中参数占位符的位置编号一定要和接口中参数的顺序保持一致 mapper:
<select id="selectUserByPage3" parameterType="map" resultType="User">
SELECT * FROM USER LIMIT #{param1},#{param2}
</select>
接口:
//参数的顺序为 [arg1, arg0...],或者[param1, param2...]
List<User> selectUserByPage3(int offset, int pagesize);
测试:
@Test
public void testSelectUserByPage3() {
List<User> users = mapper.selectUserByPage3(1, 1);
for (int i = 0; i < users.size(); i++) {
System.out.println(users.get(i));
System.out.println("----------------------");
}
}
9.返回Map类型查询结果
Mybatis中查询结果集为Map的功 能,只需要重写ResultHandler接口,,然后用SqlSession 的select方法,将xml里面的映射文件的返回值配置成 HashMap 就可以了。具体过程如下
9.1 xml文件配置
<resultMap id="resultMap1" type="HashMap">
<result property="key" column="r1" />
<result property="value" column="r2" />
</resultMap>
<select id="getResult" resultMap="resultMap1">
select count(*) r1, max(birthday) r2 from user
</select>
接口:
public HashMap<String, Object> getResult();
测试:
@Test
public void test8(){
HashMap<String, Object> result = mapper.getResult();
System.out.println(result);
}
10.解决数据库字段和实体类属性不同
在平时的开发中,我们表中的字段名和表对应实体类的属性名称不一定都是完全相同的,下面来演示一下这种情况下的如何解决字段名与实体类属性名不相同的冲突。
上面的测试代码演示当实体类中的属性名和表中的字段名不一致时,使用MyBatis进行查询操作时无法查询出相应的结果的问题以及针对问题采用的两种办法:
-
解决办法一: 通过在查询的sql语句中定义字段名的别名,让字段名的别名和实体类的属性名一致,这样就可以表的字段名和实体类的属性名一一对应上了,这种方式是通过在sql语句中定义别名来解决字段名和属性名的映射关系的。
-
解决办法二: 通过<resultMap>来映射字段名和实体类属性名的一一对应关系。这种方式是使用MyBatis提供的解决方式来解决字段名和属性名的映射关系的。
11.MyBatis整体架构
Mybatis是一个类似于Hibernate的ORM持久化框架,支持普通SQL查询,存储过程以及高级映射。Mybatis通过使用简单的XML或注解用于配置和原始映射,将接口和POJO对象映射成数据库中的记录。 由于Mybatis是直接基于JDBC做了简单的映射包装,所有从性能角度来看: JDBC > Mybatis > Hibernate
1、配置2类配置文件,其中一类是:Mybatis-Config.xml (名字不是写死,随便定义),另一类:Mapper.xml(多个),定义了sql片段;
2、通过配置文件得到SqlSessionFactory
3、通过SqlSessionFactory得到SqlSession(操作数据库)
4、通过底层的Executor(执行器)执行sql,Mybatis提供了2种实现,一种是基本实现,另一种带有缓存功能的实现;