一、什么是Mybatis
这里借用官网的一句话介绍什么是mybatis:MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。
二、Mybatis相对JDBC有哪些优势
首先我们来看一看jdbc连接数据库的连接方法:
public static void main(String[] args) {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
//1、加载数据库驱动
Class.forName("com.mysql.jdbc.Driver");
//2、通过驱动管理类获取数据库链接
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis", "root", "root");
//3、定义sql语句 ?表示占位符
String sql = "select * from user where username = ?";
//4、获取预处理statement
preparedStatement = connection.prepareStatement(sql);
//5、设置参数,第一个参数为sql语句中参数的序号(从1开始),第二个参数为设置的参数值
preparedStatement.setString(1, "王五");
//6、向数据库发出sql执行查询,查询出结果集
resultSet = preparedStatement.executeQuery();
//7、遍历查询结果集
while(resultSet.next()){
User user
System.out.println(resultSet.getString("id")+" "+resultSet.getString("username"));
}
} catch (Exception e) {
e.printStackTrace();
}finally{
//8、释放资源
if(resultSet!=null){
try {
resultSet.close();//释放结果集
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(preparedStatement!=null){
try {
preparedStatement.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(connection!=null){
try {
connection.close();//关闭数据库连接
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
通过上面的一段jdbc连接数据代码,我们看有哪些不好的地方:
1.在创建connection的时候,存在硬编码问题(也就是直接把连接信息写死,不方便后期维护)
2.preparedStatement对象在执行sql语句的时候存在硬编码问题。
3.每次在进行一次数据库连接后都会关闭数据库连接,频繁的开启/关闭数据连接影响性能。
简单的说一下mybatis相对jdbc的优势:
1.mybatis是把连接数据库的信息都是写在配置文件中,因此不存在硬编码问题,方便后期维护。
2.mybatis执行的sql语句都是通过配置文件进行配置,不需要写在java代码中。
3.mybatis的连接池管理、缓存管理等让连接数据库和查询数据效率更高。
........
三、Mybatis框架的原理介绍
这里就通过一张图来对mybatis框架原理进行介绍吧:
四、Mybatis全局配置文件
SqlMapConfig.xml是Mybatis的全局配置文件,它的名称可以是任意,但是一般命名都为(SqlMapConfig)
4.1.全局配置文件的类容和顺序
Properties(属性)
Settings(全局参数设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境信息集合)
(单个环境信息)
(事物)
(数据源)
mappers(映射器)
4.2.常见配置详解
properties标签:
Mybatis可以通过该标签来读取java配置信息:
例如在工程中对数据源信息写在db.properties文件中,可以通过properties标签来加载该文件。
db.properties:
db.driver=com.mysql.jdbc.Driver
db.url=jdbc:mysql://localhost:3306/mybatis
db.username=root
db.password=12345678
SqlMapConfig.xml使用properties标签:
<!-- 通过properties标签,读取java配置文件的内容 -->
<properties resource="db.properties" />
<!-- 配置mybatis的环境信息 -->
<environments default="development">
<environment id="development">
<!-- 配置JDBC事务控制,由mybatis进行管理 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源,采用dbcp连接池 -->
<dataSource type="POOLED">
<property name="driver" value="${db.driver}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
</dataSource>
</environment>
</environments>
注意:
1、 先加载properties中property标签声明的属性
因此在property中的name属性的值和value比properties中的resource属性先加载。后加载的db.properties会覆盖于property加载属性和值。
<properties resource="db.properties">
<property name="db.username",value="1234"/>
</properties>
2、 再加载properties标签引入的java配置文件中的属性
3、 parameterType的值会和properties的属性值发生冲突。因此,在properties文件里的内容命名最好加上db.代表是跟数据源相关的属性,这样就不容易跟以后的属性发生冲突。
settings标签:
该标签时mybatis的全局设置,该设置会影响mybatis的运行。
一般我们使用使用该标签来开启二级缓存和懒加载。
以下是几张settings配置项的说明:
typeAliases标签
该标签是对po类进行别名设置,这样,在后面使用po类的时候就可以直接通过别名引用,而不需要通过po类的全限定名来引用。这样可以提高我们的开发效率。
首先介绍下Mybatis的默认提供的别名有:
别名 | 映射的类型 |
_byte | byte |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | String |
byte | Byte |
long | Long |
short | Short |
int | Integer |
integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | Date |
decimal | BigDecimal |
bigdecimal | BigDecimal |
自定义单个别名:这种方式只能定义单个类的别名。
下面的代码就是把com.lc.mybatis.po.User类定义为user的别名
<typeAliases>
<!-- 设置单个别名 -->
<typeAlias type="com.lc.mybatis.po.User" alias="user"/>
</typeAliases>
自定义之批量定义别名:
下面代码是把com.lc.mybatis.po类下的所有类都声明别名,默认的别名就是类名(类名大小写都可以)
<!-- 设置别名 -->
<typeAliases>
<!-- 批量设置别名 -->
<!-- package:指定包名称来为该包下的po类声明别名,默认的别名就是类名(类名首字母大小写都可以) -->
<package name="com.lc.mybatis.po"/>
</typeAliases>
mappers标签
该标签的作用是加载映射文件
方式一:<mapper resource=""/>
该方式是加载相对于类路径下的映射文件:
<mappers>
<mapper resource="sqlmap/User.xml"/>
</mappers>
方式二:<mapper url=""/>
该方式使用全限定路径
<mapper url="file:///D:\workspace_spingmvc\mybatis_01\config\sqlmap\User.xml" />
方式三:<mapper class=""/>
该方式使用mapper接口的全限定类名
<mapper class="cn.itcast.lc.mapper.UserMapper"/>
此方式要求:
Mapper接口Mapper映射文件名称相同且在同一个目录下。
方式四:<package name=""/>
该方式是加载指定包下的所有映射文件
<package name="cn.lc.mybatis.mapper"/>
此方式要求:
Mapper接口Mapper映射文件名称相同且在同一个目录下。
五、映射文件
5.1.输入映射parameterType
第一种:简单类型
#{}表示占位符?,parameterType接收简单类型的参数时,里面的名称可以任意
<select id="findUserById" parameterType="java.lang.Integer" resultType="user">
SELECT * FROM USER WHERE id = #{id}
</select>
${}表示拼接符,parameterType接收简单类型的参数时,里面的名称必须是value
<select id="findUsersByName" parameterType="java.lang.String" resultType="com.lc.mybatis.po.User">
SELECT * FROM USER WHERE username LIKE "%${value}%"
</select>
第二种:pojo类型
这里通过用户的用户名进行模糊查询演示pojo类型
在映射文件中添加模糊查询语句:
<!-- parameterType传递pojo类型 -->
<select id="findUsersByPojo" parameterType="com.lc.mybatis.po.User" resultType="com.lc.mybatis.po.User">
SELECT * FROM USER WHERE username LIKE "%${username}%"
</select>
user类
public class User {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
}
测试类:
public class UserDao{
//根据用户名进行模糊查询
@Override
public List<User> findUserByPojo(){
//全局配置文件路径:
String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
//创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = new User();
user.setUsetname("张三");
List<User> users = (List<User>)sqlSession.selectList("test.findUsersByPojo",user);//传入pojo
System.out.println(users);
sqlSession.close();
return users;
}
}
第三种:包装类型pojo
这里通过用户名和用户地址对用户进行查询来演示包装类型pojo:
首先创建包装pojo类:
public class UserVO {
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
在映射文件中添加查询语句:
<!-- parameterType传递pojo包装类型 -->
<select id="findUsersByPojo1" parameterType="com.lc.mybatis.po.UserVO" resultType="user">
SELECT * FROM USER WHERE username LIKE "%${user.username}%" AND address=#{user.address}
</select>
测试类:
public class UserDao{
//根据用户名和地址进行查询
@Override
public List<User> findUserByPojo1(){
//全局配置文件路径:
String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
//创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = new User();
user.setUsetname("张三");
user.setAddress("郝汉山");
UserVO userVo = new UserVO();
userVo.setUser(user);
List<User> users = (List<User>)sqlSession.selectList("test.findUsersByPojo1",userVo);//传入pojo包装类
System.out.println(users);
sqlSession.close();
return users;
}
}
第四种:map集合类型
这里通过查询用户信息演示:
在映射文件中添加该查询语句:
<!-- parameterType传递hashmap类型 -->
<select id="findUsersByMap" parameterType="java.util.Map" resultType="user">
SELECT * FROM USER WHERE username LIKE "%${username}%" AND address=#{address}
</select>
测试方法:
public class UserDao{
//根据用户名和地址进行查询
@Override
public List<User> findUserByMap(){
//全局配置文件路径:
String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
//创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
Map<String,String> map = new HashMap<>();
map.put("username","张三");
map.put("address","郝汉山");
List<User> users = (List<User>) sqlSession.selectList("test.findUsersByMap",map);//传入pojo包装类
System.out.println(users);
sqlSession.close();
return users;
}
}
5.2.resultType结果映射
resultType结果映射要求:需要查询结果的列名和映射的对象的属性名一致,这样才能映射成功。如果映射没成功也不会报错,只是映射结果中对象的相应属性没有值,为空。如果映射的列名和对象中的属性名全部不一致,那么映射的对象为空。如果在使用sql语句查询的时候给查询结果列设置了别名,则别名要和映射结果对象的属性名一致,这样才能保证映射成功。
第一种:简单类型
注意: 如果结果映射为简单类型,则需要查询的结果为一列才能映射成功。
例如:查询用户表中用户的总数。
映射文件为:
<!-- resultType:输出为简单类型 -->
<select id="findUserCount" resultType="int">
select count(*) from user;
</select>
测试代码:
public class UserDao{
@Override
public int findUserCount(){
//全局配置文件路径:
String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
//创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
int userCount= sqlSession.selectOne("test.findUserCount");
System.out.println(userCount);
sqlSession.close();
return userCount;
}
}
第二种:pojo结果映射
这里操作pojo输入映射。
5.3.resultMap结果映射
使用resultMap结果映射时,不需要查询出来的结果集的列名和映射结果对象的属性名相同,但是需要声明一个resultMap,手动的方式来对列名和对象属性进行映射。(resultMap一般用于多表关联映射)
例如:通过查询用户表中的用户,并对查询出来的用户表的列名设置别名。
映射文件添加查询语句:
[id]:定义resultMap的唯一标识
[type]:定义该resultMap最终映射的pojo对象
[id标签]:映射结果集的唯一标识列,如果是多个字段联合唯一,则定义多个id标签
[result标签]:映射结果集的普通列
[column]:SQL查询的列名,如果列有别名,则该处填写别名
[property]:pojo对象的属性名
<!-- 如果查询出来的列名有别名就不能通过resultType来接收输出类型了。需要通过resultMap来声明传出类型(resultMap需要声明) -->
<resultMap type="user" id="userMap">
<!-- id标签:专门查询结果中唯一列映射 -->
<id column="id_" property="id"/>
<!-- result标签:映射查询结果中的普通列 -->
<result column="username_" property="username"/>
<result column="address_" property="address"/>
</resultMap>
<select id="findUserResultMap" parameterType="int" resultMap="userMap">
select id id_,username username_,address address_ from user where id=#{id}
</select>
测试类:
public class UserDao{
@Override
public User findUserResultMap(){
//全局配置文件路径:
String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
//创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = sqlSession.selectOne("test.findUserResultMap",1);
System.out.println(user);
sqlSession.close();
return user;
}
}
5.4.动态sql
在mybatis中提供了一些动态sql标签,可以让我们开发效率更快,这些动态sql语句可以增加我们写的sql语句的重用性,常用的动态sql语句标签有:if标签、sql片段(需要先定义后使用)、where标签、foreach标签
通过下面例子来演示动态sql常用的标签:
需求分析:查询用户表中相关的用户。
发出相关的sq语句有:select * from user where username like "%张三%" and address= ?;
select * from user;
select * from user where id in(1,2,10);
如何才能让这些语句在我们需要的时候就使用,不需要的时候就不适用。其实我们发现这三条语句有很多重复的地方,我们如果能做到让重复的能够重复使用,没有重复的可以按我们需求使用的话就能减少我们写很多sql语句。当然动态sql就是帮我们解决这些问题的。
映射文件:
<!-- 定义sql片段,用于可重用的sql片段 -->
<sql id="whereClause">
<!-- if标签:可以对输入的参数进行判断 -->
<!-- test:指定判断表达式 -->
<if test="user!=null">
<if test="user.username!=null and user.username!=''">
AND username LIKE '%${user.username}%'
</if>
<if test="user.address!=null and user.address!=''">
AND address=#{user.address}
</if>
</if>
<if test="idList!=null">
AND id IN
<!-- foreach标签: 可以循环传入参数值 -->
<!-- collenction:标示pojo中集合属性的属性名称 -->
<!-- item:每次遍历出来的对象 -->
<!--open开始遍历时拼接的串-->
<!--close结束遍历时拼接的串-->
<!--separator遍历每个对象之间需要拼接字符-->
<foreach collection="idList" item="item" open="(" close=")" separator=",">
#{item}
</foreach>
</if>
</sql>
<select id="findUserList" parameterType="userVO" resultType="user">
SELECT * FROM USER
<!-- where标签: 默认去掉第一个AND,如果没有参数就去掉where自己-->
<where>
<!--引用sql语句片段-->
<include refid="whereClause"></include>
</where>
</select>
userVo类:
public class UserVO {
private User user;
private List<Integer> idList;
public List<Integer> getIdList() {
return idList;
}
public void setIdList(List<Integer> idList) {
this.idList = idList;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
测试类:
public class UserDao{
@Override
public void findUserList(){
//全局配置文件路径:
String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
//创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
//测试foreach语句执行
UserVO userVo = new UserVO();
List<Integer> idList = new ArrayList<>();
idList.add(1);
idList.add(2);
idList.add(3);
userVo.setIdList(idList);
//测试select * from user where username like ? and address=?;
//User user = new User();
//user.setUsername("张三");
//user.setAddress("武当山");
//userVo.setUser(user);
List<User> users = sqlSession.selectOne("test.findUserList",userVo);
System.out.println(users);
sqlSession.close();
}
}
六、Mybatis之传统Dao层的开发(该方式很少用)
6.1.开发环境准备:
1.个人使用的jdk为1.7
2.开发工具是使用的Eclipse4.5
3.数据库使用的MySQL5X
4.导入mybatis所需要的jar包
5.导入mysql数据库驱动包
6.2.需求分析:
根据ID查询查询用户、根据用户名称进行模糊查询、添加用户
6.3.代码实现
创建用户表对应pojo类User:
public class User {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
//setter和get方法省略
}
创建mybatis的全局配置文件SqlMapConfig.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>
<!-- 配置mybatis的环境信息 -->
<environments default="developments">
<environment id="developments">
<!-- 配置JDBC事务控制,由mybatis进行管理 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源,采用mybatis连接池 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="12345678"/>
</dataSource>
</environment>
</environments>
<!-- 加载映射文件 -->
<mappers>
<mapper resource="User.xml"/>
</mappers>
</configuration>
创建需求开发的映射文件User.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:命名空间,它的作用就是对SQL进行分类化管理,可以理解为SQL隔离
注意:使用mapper代理开发时,namespace有特殊且重要的作用
-->
<mapper namespace="test">
<!-- 根据用户ID查询用户信息 -->
<!-- select:表示一个MappedStatement对象 -->
<!-- id:statement的唯一标识 -->
<!-- #{}:表示一个占位符?-->
<!-- #{id}:里面的id表示输入参数的参数名称,如果该参数为简单类型,那么#{}里面的参数可以任意 -->
<!-- parameterType:输入参数的java类型 -->
<!-- resultType:输出结果的所映射的java类型(单条结果所对应的java类型) -->
<select id="findUserById" parameterType="java.lang.Integer" resultType="com.lc.mybatis.po.User">
SELECT * FROM USER WHERE id = #{id}
</select>
<!-- 根据用户名进行模糊查询 -->
<!-- ${}:表示一个sql连接符 -->
<!-- ${value}:里面的 value表示输入的名称,如果该参数是简单类型,那么${}里面的参数名称必须是value-->
<!-- ${}这种写法存在sql注入的风险,所以需要 慎用!但是有些场景需要用到比如:排序,-->
<select id="findUsersByName" parameterType="java.lang.String" resultType="com.lc.mybatis.po.User">
SELECT * FROM USER WHERE username LIKE "%${value}%"
</select>
<!-- 添加用户: -->
<!--
[selectKey标签]:通过select查询来生成主键
[keyProperty]:指定存放生成主键的属性
[resultType]:生成主键所对应的Java类型
[order]:指定该查询主键SQL语句的执行顺序,相对于insert语句
[last_insert_id]:MySQL的函数,要配合insert语句一起使用,该函数在mysql中是执行在insert语句后。
-->
<insert id="insertUser" parameterType="com.lc.mybatis.po.User">
<selectKey keyProperty="id" resultType="int" order="AFTER">
SELECT LAST_INSERT_ID()
</selectKey>
INSERT INTO USER(username,sex,birthday,address) VALUES(#{username},#{sex},#{birthday},#{address})
</insert>
</mapper>
dao层接口:
public interface UserDao {
//根据用户id查询用户
public User findUserById(int id);
//根据用户名进行模糊查询
public List<User> findUsersByName(String name);
//添加用户
public void addUser(User user);
}
dao层的实现:
public class UserDaoImpl implements UserDao{
//通过构造方法注入sqlSessionFactory
private SqlSessionFactory sqlSessionFactory;
public UserDaoImpl(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
}
//根据id查询用户
@Override
public User findUserById(int id) {
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = sqlSession.selectOne("test.findUserById", id);
System.out.println(user);
sqlSession.close();
return user;
}
//根据用户名进行模糊查询
@Override
public List<User> findUsersByName(String name){
SqlSession sqlSession = sqlSessionFactory.openSession();
List<User> users = sqlSession.selectList("test.findUsersByName",name);
System.out.println(users);
sqlSession.close();
return users;
}
//添加用户
public List<User> addUser(String name){
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = new User();
user.setUsername("超哥2");
user.setAddress("成都市");
//调用SqlSession的增删改查方法
//第一个参数:表示statement的唯一表示
//第一个参数:表示占位符的
sqlSession.insert("test.insertUser", user);
System.out.println(user.getId());
//切记:增删改操作需要提交事务。
sqlSession.commit();
//关闭资源
sqlSession.close();
}
测试类:这里就只通过根据id查找用户进行测试
public class UserDaoTest {
private SqlSessionFactory sqlSessionFactory;
@Before
public void setUp() throws IOException {
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void testFindUserById() {
UserDao userDao = new UserDaoImpl(sqlSessionFactory);
userDao.findUserById(1);
}
}
七、Mybatis之Mapper接口的开发方式
该方式开发,不需要写dao层的实现类,而是mybatis根据映射文件等信息对接口进行jdk动态代理生成代理类来实现接口中的方法,因此,采用这种方式,我们只需要编辑接口,而不需要去写实现。
7.1.需求分析
根据id查询用户。
7.2.Mapper开发代理规范
1、mapper接口的全限定名要和mapper映射文件的namespace值一致。
2、mapper接口的方法名称要和mapper映射文件的statement的id一致。
3、mapper接口的方法参数类型要和mapper映射文件的statement的parameterType的值一致,而且它的参数是一个。
4、mapper接口的方法返回值类型要和mapper映射文件的statement的resultType的值一致。
7.3.代码实现
准备po类:
public class User {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
//getter和setter方法省略
}
Mapper接口:
public interface UserMapper {
public User findUserById(int id);
}
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:命名空间,它的作用就是对SQL进行分类化管理,可以理解为SQL隔离
注意:使用mapper代理开发时,namespace有特殊且重要的作用
-->
<mapper namespace="com.lc.mybatis.mapper.UserMapper">
<!-- 根据用户ID查询用户信息 -->
<!-- select:表示一个MappedStatement对象 -->
<!-- id:statement的唯一标识 -->
<!-- #{}:表示一个占位符?-->
<!-- #{id}:里面的id表示输入参数的参数名称,如果该参数为简单类型,那么#{}里面的参数可以任意 -->
<!-- parameterType:输入参数的java类型 -->
<!-- resultType:输出结果的所映射的java类型(单条结果所对应的java类型) -->
<select id="findUserById" parameterType="java.lang.Integer" resultType="com.lc.mybatis.po.User">
SELECT * FROM USER WHERE id = #{id}
</select>
</mapper>
在全局配置文件SqlMapperConfig中添加该映射文件
测试代码:
public class UserMapperTest {
private SqlSessionFactory sqlSessionFactory ;
@Before
public void setUp() throws IOException {
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void testFindUserById() {
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取UserMapper的代理类
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.findUserById(10);
System.out.println(user);
sqlSession.close();
}
}
八、Mybatis一级缓存
mybatis提供查询缓存,如果缓存中有数据,就不用从数据库中获取,用于减轻数据压力,提高系统性能
一级缓存是sqlSession级别的缓存,在操作数据库的时候,需要构造sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的SqlSession的缓存区域(HashMap)是互相不受影响的。
mybatis默认是支持一级缓存的。
8.1.证明一级缓存的存在:
验证方法:
public void testOneLevelCache() {
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取UserMapper的代理类
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//第一次查询
User user = userMapper.findUserById(10);
System.out.println(user);
//第二次查询
User user2 = userMapper.findUserById(10);
System.out.println(user2);
sqlSes
通过执行该方法,查看打印日志可以看出就执行了一次sql语句的查询,因此可以判断第二次查询是没有从数据库中进行查询的。
那当什么时候一级缓存会被清空呢?
通过测试发现,当在第二次查询数据库之前对数据库进行了写的操作后,第二次查询就不会从缓存中查询结果,而是直接从数据中查询结果。另一种就是在第一查询结果后,手动的方式清空一级缓存(使用sqlSession.clearCache();方法。)
测试方法:
九、Mybatis二级缓存
二级缓存是Mapper级别的缓存。多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是夸SqlSession的。(二级缓存Mybatis默认是关闭的,需要自己去手动配置开启或可以自己选择用哪个厂家的缓存来作为二级缓存)
9.1.开启二级缓存
首先在全局配置文件中配置开启二级缓存功能:
<!-- 开启二级缓存总开关 -->
<setting name="cacheEnabled" value="true"/>
在映射文件中去选择是否该映射文件使用二级缓存:
如果使用就进行以下配置,如果不是用,就不需要对映射文件做任何修改。
<!-- 开启本mapper下的namespace的二级缓存,默认使用的是mybatis提供的PerpetualCache -->
<cache></cache>
也可以设置选择二级缓存提工商
以下是我选择使用EhcacheCache缓存(注意:在使用EhcacheCache缓存需要导入jar包)
最后就是,在需要缓存的po类中去实现序列化:
。
验证二级缓存是否开启:
验证方法如下:
@Test
//测试二级缓存是否开启
public void testTwoLevelCache() {
SqlSession sqlSession1 = sqlSessionFactory.openSession();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
SqlSession sqlSession3 = sqlSessionFactory.openSession();
//获取UserMapper的代理类
UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
UserMapper userMapper3 = sqlSession3.getMapper(UserMapper.class);
User user1 = userMapper1.findUserById(10);
System.out.println(user1);
//当sqlSession1执行close()的时候,才把sqlSession1查询的结果写到二级缓存中去。
sqlSession1.close();
// User u = new User();
// u.setUsername("小胖");
// u.setAddress("成都");
// u.setSex("2");
// u.setBirthday(new Date());
// userMapper3.insertUser(u);
// //当其中任何一个sqlSession执行commit()方法后将刷新整个缓存区
// sqlSession3.commit();
// sqlSession3.close();
//第二次查询
User user2 = userMapper2.findUserById(10);
System.out.println(user2);
sqlSession2.close();
}
测试结果:
同时通过测试,当在第二次查询的时候,向数据库提交数据后,就会对缓存进行刷新,这样在第二次查询的时候就没有走缓存,而是走的数据库。
9.2.禁用二级缓存
由于二级缓存默认是关闭的,如果不开启就不会使用二级缓存。如果,我们开启了二级缓存,而在有些查询结果集中,不需要受到二级缓存影响,该怎么去做呢?
在select查询中,默认是使用了二级缓存,如果不想使用二级缓存,就在select标签中有一个useCache的属性设置为false,就代表不使用二级缓存,每次进行查询数据都不会从缓存总获取,而是直接从数据库中进行查询。useCache的默认值是true,即代表statement使用二级缓存。
9.3.刷新二级缓存
在statement中设置flushCache=true,可以刷新二级缓存。默认情况下,select语句中的flushCache是false。如果是insert、update、delete语句,那么flushCache的默认值是true。如果将select语句中的flushCache值改为true,就意味着查询语句的二级缓存失效,每次查询都会从数据库进行查询。如果将select语句的flushCache值为false,就代表该查询语句使用了二级缓存,如果在数据库中修改了数据,而二级缓存中的数据还是原来的数据,那么这样就会出现脏读。
flushCache设置如下: