Mybatis之——基本使用

MyBatis 框架概述

mybatis 是一个优秀的基于 java 的持久层框架,它内部封装了 jdbc,使开发者只需要关注 sql 语句本身,
而不需要花费精力去处理加载驱动、创建连接、创建 statement 等繁杂的过程。
mybatis 通过 xml 或注解的方式将要执行的各种 statement 配置起来,并通过 java 对象和 statement 中
sql 的动态参数进行映射生成最终执行的 sql 语句,最后由 mybatis 框架执行 sql 并将结果映射为 java 对象并
返回。
采用 ORM 思想解决了实体和数据库映射的问题,对 jdbc 进行了封装,屏蔽了 jdbc api 底层访问细节,使我
们不用与 jdbc api 打交道,就可以完成对数据库的持久化操作。

jdbc 程序的回顾

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public static void main(String[] args) {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
//加载数据库驱动
Class.forName("com.mysql.jdbc.Driver");

//通过驱动管理类获取数据库链接
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8","root", "root");

//定义 sql 语句 ?表示占位符
String sql = "select * from user where username = ?";

//获取预处理 statement
preparedStatement = connection.prepareStatement(sql);

//设置参数,第一个参数为 sql 语句中参数的序号(从 1 开始),第二个参数为设置的参数值
preparedStatement.setString(1, "王五");

//向数据库发出 sql 执行查询,查询出结果集
resultSet = preparedStatement.executeQuery();

//遍历查询结果集
while(resultSet.next()){
System.out.println(resultSet.getString("id") + resultSet.getString("username"));
}
} catch (Exception e) {
e.printStackTrace();
}finally{
//释放资源
if(resultSet!=null){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(preparedStatement!=null){
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(connection!=null){
try {
connection.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

我的第一个Mybatis项目

maven项目配置文件:pox.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<?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">


<?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>pers.mrLiu.mybatisTest</groupId>
<artifactId>myBatisTest</artifactId>
<version>1.0-SNAPSHOT</version>

<!--设置项目构建源的编码,并且指定jdk版本为1.8-->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>

<dependencies>
<!--导入mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.4</version>
</dependency>
<!--导入log4j日志(mybatis使用)-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<!--导入mysql的驱动包(JDBC)-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<!--测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
</dependencies>


</project>

Mybatis配置文件:SqlMapConfig.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
<?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属性: 常用的
用于指定配置文件的位置,是按照类路径的写法来写,并且必须存在于类路径下。
url属性:
是要求按照Url的写法来写地址
URL:Uniform Resource Locator 统一资源定位符。它是可以唯一标识一个资源的位置。
它的写法:
http://localhost:8080/mybatisserver/demo1Servlet
协议 主机 端口 URI

URI:Uniform Resource Identifier 统一资源标识符。它是在应用中可以唯一定位一个资源的。
-->

<properties Resource="jdbcConfig.properties">
<!--
这些不用填,吃饱了没事干可以这样填
<property name="driver" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/eesy_mybatis"></property>
<property name="username" value="root"></property>
<property name="password" value="1234"></property>
-->
</properties>





<!--使用typeAliases配置别名,它只能配置domain中类的别名 -->
<typeAliases>
<!--
typeAlias用于配置别名。type属性指定的是实体类全限定类名。alias属性指定别名,当指定了别名就不再区分大小写
<typeAlias type="pers.mrLiu.mybatisTest.domain.User" alias="user"></typeAlias>
-->


<!--ps:这种方式更方便-->
<!-- 用于指定要配置别名的包,当指定之后,该包下的实体类都会注册别名,并且类名就是别名,不再区分大小写-->
<package name="pers.mrLiu.mybatisTest.domain"></package>
</typeAliases>





<!-- 配置 mybatis 的环境 -->
<environments default="mysql">
<!-- 配置 mysql 的环境 -->
<environment id="mysql">
<!-- 配置事务的类型 -->
<transactionManager type="JDBC"/>
<!-- 配置连接数据库的信息:用的是数据源(连接池) -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://47.100.77.53/jvtcGroupOfRobot"/>
<property name="username" value="jvtcGroupOfRobot"/>
<property name="password" value="aA2428933105."/>
</dataSource>

<!--
如果使用了外部jdbc配置配置文件,就要这样写:${配置中的属性名}
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
-->

</environment>
</environments>
<!-- 告知 mybatis 映射配置的位置 -->
<mappers>
<!-- 如需使用注解 -->
<!-- 则不需要下面的的映射配置文件,只需要在接口上加上注解 -->
<!-- 并且把上方的映射配置改成下方配置即可 -->
<!-- <mapper class="pers.mrLiu.mybatisTest.dao.UserDao"/> -->
<!--<mapper resource="pers/mrLiu/mybatisTest/dao/UserDao.xml"/> -->


<!-- package标签是用于指定dao接口所在的包,当指定了之后就不需要在写mapper以及resource或者class了 -->
<!-- 这种更方便 -->
<package name="com.itheima.dao"></package>
</mappers>
</configuration>

Mybatis映射配置(不使用xml配置映射则不需要此文件):对应着每个dao接口文件,例如:UserDao.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?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="pers.mrLiu.mybatisTest.dao.UserDao">

<!-- 如果设置了typeAliases标签,则可以这样使用,所有需要用到user类的都可以这样 -->
<!-- <select id="findAll" resultType="User"> -->


<!-- 根据sql语句不得不同需要换相应的标签 -->
<select id="findAll" resultType="pers.mrLiu.mybatisTest.domain.User">
select * from user;
</select>
</mapper>

jdbcConfig.Properties配置(不使用外部JDBC配置文件则不需要)

1
2
3
4
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/eesy_mybatis
username=root
password=1234

log4j日志配置文件:log4j.properties

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Set root category priority to INFO and its only appender to CONSOLE.
#log4j.rootCategory=INFO, CONSOLE debug info warn error fatal
log4j.rootCategory=debug, CONSOLE, LOGFILE

# Set the enterprise logger category to FATAL and its only appender to CONSOLE.
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE

# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n

# LOGFILE is set to be a File appender using a PatternLayout.
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=d:\axis.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n


UserDao接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package pers.mrLiu.mybatisTest.dao;

import pers.mrLiu.mybatisTest.domain.User;

import java.util.List;

public interface UserDao {

/**
* 查询所有用户
* @return
*/
//如需注解配置则输入:@Select("select * from user")
List<User> findAll();
}

调用Mybatis框架DAO层实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//读取mybatis的xml配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");

//创建构建工厂的构建者对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();

//通过构建者对象创建工厂对象
SqlSessionFactory build = sqlSessionFactoryBuilder.build(resourceAsStream);

//通过工厂对象创建SqlSession对象
SqlSession sqlSession = build.openSession();

//通过sqlSession对象创建一个代理对象
UserDao userDao = sqlSession.getMapper(UserDao.class);

//查询所有
List<User> users = userDao.findAll();

//遍历所有
users.forEach(System.out::println);



/*
当执行增删改时,执行后没有效果,原因是什么?
这一点和 jdbc 是一样的,我们在实现增删改时一定要去控制事务的提交,那么在 mybatis 中如何控制事务提交呢?
可以使用:session.commit();来实现事务提交。
*/

Mybatis框架的执行流程

Mybatis框架的执行流程

Mybatis框架执行原理

1.读取核心配置文件并返回InputStream流对象。
2.根据InputStream流对象解析出Configuration对象,然后创建SqlSessionFactory工厂对象
3.根据一系列属性从SqlSessionFactory工厂中创建SqlSession
4.从SqlSession中调用Executor执行数据库操作&&生成具体SQL指令
5.对执行结果进行二次封装
6.提交与事务

Mybatis映射配置中SQL语句#{}${}的使用(面试)

例1:根据ID查用户信息

1
2
3
4
<!-- 根据 id 查询 -->
<select id="findById" resultType="com.itheima.domain.User" parameterType="int">
select * from user where id = #{uid}
</select>
  • resultType 属性:结果集的类型
  • parameterType 属性:传入参数的类型
  • sql 语句中使用#{}字符:它代表占位符,相当于原来 jdbc 部分所学的?,都是用于执行语句时替换实际的数据。具体的数据是由#{}里面的内容决定的。
  • #{}中内容的写法:由于数据类型是基本类型(int),所以此处可以随意写

例2:保存用户

1
2
3
4
5
<!-- 保存用户-->
<insert id="saveUser" parameterType="com.itheima.domain.User">
insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
</insert>

  • parameterType 属性:代表参数的类型,因为我们要传入的是一个类的对象,所以类型就写类的全名称。
  • sql 语句中使用#{}字符:它代表占位符,相当于原来 jdbc 部分所学的?,都是用于执行语句时替换实际的数据
  • #{}中内容的写法:由于我们保存方法的参数是 一个 User 对象,此处要写 User 对象中的属性名称(它用的是 ognl 表达式)
  • ognl 表达式:

    它是 apache 提供的一种表达式语言,全称是:Object Graphic Navigation Language 对象图导航语言
    它是按照一定的语法格式来获取数据的。
    语法格式就是使用 #{对象.对象}的方式

    #{user.username}它会先去找 user 对象,然后在 user 对象中找到 username 属性,并调用getUsername()方法把值取出来。但是我们在 parameterType 属性上指定了实体类名称,所以可以省略 user.而直接写 username

例3:新增用户后获取自增id的返回值

新增用户后,同时还要返回当前新增用户的 id 值,因为 id 是由数据库的自动增长来实现的,所以就相当于我们要在新增后将自动增长 auto_increment 的值返回

1
2
3
4
5
6
7
8
9
10
11
<insert id="saveUser" parameterType="USER">

<!-- 配置保存时获取插入的 id -->
<selectKey keyColumn="id" keyProperty="id" resultType="int" order="AFTER">
select last_insert_id();
</selectKey>

insert into user(username,birthday,sex,address)
values(#{username},#{birthday},#{sex},#{address})

</insert>
  • keyColumn:数据库中列名
  • keyProperty:数据模型JavaBean对象的属性名
  • order:执行SQL语句之前还是之后再执行此标签内容,AFTE:之后, BEFORE:之前

#{}${}的区别

#{}表示一个占位符号

通过#{}可以实现 preparedStatement 向占位符中设置值,自动进行 java 类型和 jdbc 类型转换,
#{}可以有效防止 sql 注入。 #{}可以接收简单类型值或 pojo 属性值。
如果 parameterType 传输单个简单类型值(基本数据类型:String、int等),#{}括号中可以是 value 或其它名称(随便输入)。

${}表示拼接 sql 串

通过${}可以将 parameterType 传入的内容拼接在 sql 中且不进行 jdbc 类型转换
${}可以接收简单类型值或 pojo 属性值
如果 parameterType 传输单个简单类型值,${}括号中只能是 value

resultMap 结果类型

resultMap 标签可以建立查询的列名和实体类的属性名称不一致时建立对应关系。从而实现封装。
在 select 标签中使用 resultMap 属性指定引用即可。同时 resultMap 可以实现将查询结果映射为复杂类
型的 pojo,比如在查询结果映射对象中包括 pojo 和 list 实现一对一查询和一对多查询。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!-- 建立 User 实体和数据库表的对应关系
type 属性:指定实体类的全限定类名
id 属性:给定一个唯一标识,是给查询 select 标签引用用的。
-->

<resultMap type="com.itheima.domain.User" id="userMap">
<id column="id" property="userId"/>
<result column="username" property="userName"/>
<result column="sex" property="userSex"/>
<result column="address" property="userAddress"/>
<result column="birthday" property="userBirthday"/>
</resultMap>

<!-- ps:注意<select>等标签的resultType属性都需要改成resultMap属性 -->
<select id="findById" resultMap="userMap" parameterType="int">
select * from user where id = #{uid}
</select>

  • id 标签:用于指定主键字段
  • result 标签:用于指定非主键字段
  • column 属性:用于指定数据库列名
  • property 属性:用于指定实体类属性名称

扩展(面试题):为什么实体类(user类)要实现Serializable接口呢?

网上查了一下资料,如下:

Serializable接口是一个里面什么都没有的接口
它的源代码是public interface Serializable{},即什么都没有。

如果一个接口里面什么内容都没有,那么这个接口是一个标识接口
比如,一个学生遇到一个问题,排错排了几天也没解决,此时,她举手了(示意我去帮他解决),然后我过去,帮他解决了,那么这个举手其实就是一个标识,自己不能解决的问题标示我去帮他解决

在Java中的这个Serializable接口是给JVM看的,告诉JVM,我不做这个类的序列化了,你(JVM)给我序列化,序列化就是变成二进制流,比如云计算、Hadoop,特别是Hadoop完全就是分布式环境,那么就要涉及到对象要在网络中传输,里面的全是二进制流,当然你来做这个序列化操作也可以,但是这个类里面可能还有一个类,如果你把外面的类对象Person变成二进制,那么里面也要序列化(这要用到深度遍历,很麻烦),干脆告诉JVM,让他来帮你做。