一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第12天,点击查看活动详情。
MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。
iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAO)
MyBatis ,是国内最火的持久层框架
采用了ORM思想解决了实体类和数据库表映射的问题。对JDBC进行了封装,屏蔽了JDBCAPI底层的访问细节,避免我们与jdbc的api打交 道,就能完成对数据的持久化操作。
O--Object java对象
R- Relation 关系,就是数据库中的一张表
M-mapping 映射
首先给大家介绍MyBatis的含义
MyBatis是一个支持普通sql查询,存储过程和高级映射的优秀持久层框架。MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索封装。MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录。
源码分析:一般都是从helloworld入手
1、根据xml配置文件(全局配置文件mybatis-config.xml)创建一个SqlsessionFactory对象,mybatis-config.xml有数据源一些环境信息
2、sql映射文件EmployeeMapper.xml配置了每一个sql,以及sql的封装规则等。
3、将sql映射文件注册在全局配置文件中
4、写代码:
- 根据全局配置文件得到sqlsessionFactory
- 使用SqlSession工程进行crud、sqlseesion就代表和数据库进行会话,用完close
- 使用sql标识告知mybatis来执行哪个sql,sql都是保存在sql映射文件中
测试类SqlSessionFactoryBuilder处打断点
第一步:根据mybatis-config.xml全局配置文件创建SqlSessionFactory对象、就是把配置文件的详细信息解析保存在了configuration对象中,返回包含了configuration的defaultSqsessionFactory对象
注意:mappedSatement对象代表一个增删改查的详细标签
1.Mybatis的注解开发
是使用注解将IUserDao.xml的配置进行注解
步骤:
- 环境搭建
- 单表CRUD操作(代理Dao方式)
- 多表查询的配置
最输入项目名称
下面进行设置Maven工程的配置,因为我是自己配置的所有不会出现什么麻烦
导入依赖
设置实现User类其中加入Account类是为了实现多对多的数据模式
package com.mybatisD.daomain;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
* @author :程序员徐大大
* @description:TODO
* @date :2022-01-23 14:39
*/
public class User implements Serializable {
private Integer id;
private String username;
private String address;
private String sex;
private Date birthday;
//一对多关系映射,一个用户对应多个账户
private List<Account> accounts;
public List<Account> getAccounts() {
return accounts;
}
public void setAccounts(List<Account> accounts) {
this.accounts = accounts;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", address='" + address + '\'' +
", sex='" + sex + '\'' +
", birthday=" + birthday +
'}';
}
}
设置Account类其中加入User为的是实现多对多数据模式
package com.mybatisD.daomain;
import java.io.Serializable;
/**
* @author :程序员徐大大
* @description:TODO
* @date :2022-01-23 16:57
*/
public class Account implements Serializable {
private Integer id;
private Integer uid;
private double money;
//多对一(mybatis中称之为一对一)的映射,一个账户只能属于一个用户
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getUid() {
return uid;
}
public void setUid(Integer uid) {
this.uid = uid;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", uid=" + uid +
", money=" + money +
'}';
}
}
IUserDao接口类
package com.mybatisD.dao;
import com.mybatisD.daomain.User;
import org.apache.ibatis.annotations.*;
import org.apache.ibatis.mapping.FetchType;
import java.util.List;
/**
* @author: 徐大大
* @description: TODO
* @date: 2022-01-23 14:50
* 在mybatis中针对CRUD一共有四个注解
* @Select @Insert @Update @Delete
*/
@CacheNamespace(blocking = true) //注解开启二级缓存,要在SqlMapConfig.xml里面开启二级缓存
public interface IUserDao {
@Select("select * from user")
List<User> findAll();//查询所有用户
/**
* @return
* @description: 保存用户
* @date: 2022-01-23 15:28
*/
@Insert("insert into user (username,address,sex,birthday) values(#{username},#{address},#{sex},#{birthday})")
void saveUser(User user);
/**
* @return
* @description: 更新用户
* @date: 2022-01-23 15:28
*/
@Update("update user set username=#{username} ,sex=#{sex},address=#{address},birthday=#{birthday} where id =#{id}")
void updateUser(User user);
/**
* @return
* @description: 删除用户
* @date: 2022-01-23 15:28
*/
@Delete("delete from user where id = #{id}")
void deleteUser(Integer id);
/**
* @return
* @description: 根据ID查询用户
* @date: 2022-01-23 15:28
*/
@Select("select * from user where id = #{id}")
User findById(Integer id);
/**
* @return
* @description: 根据根据用户名称模糊查询
* @date: 2022-01-23 15:28
*/
@Select("select * from user where username like #{username}")
//需要在测试类加入双%%号
// @Select("select * from user where username like '%${value}%'")
List<User> findUserByName(String username);
/**
* @return
* @description: 查询用户总数量
* @date: 2022-01-23 15:28
*/
@Select("select count(*) from user")
int findTotalUser();
/**
* @description: 根据ID查询但数据库于User实现类的id不一样
* 其中id= userMap是可以配置所有的方法中都可以使用属性的别名
* 在其他方法中使用@ResultMap(value={"userMap"})
* @date: 2022-01-23 15:28
* @return
*/
/*@Select("select * from user where id = #{id}")
@Results(id="userMap" ,value = {
@Result(id = true,column = "id" , property = "userId"),
@Result(id = true,column = "address" , property = "userAddress"),
@Result(id = true,column = "sex" , property = "userSex"),
@Result(id = true,column = "birthday" , property = "userBirthday"),
})
User findByOutherId(Integer id);*/
@Select("select * from user")
@Results(value = {
@Result(property = "accounts", column = "id", many = @Many(select = "com.mybatisD.dao.IAccount.findAccountByUid",fetchType = FetchType.LAZY))//LAZY是延迟
})
List<User> findAllAccount();//查询所有用户
}
IAccountDao接口类
package com.mybatisD.dao;
import com.mybatisD.daomain.Account;
import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.mapping.FetchType;
import java.util.List;
public interface IAccountDao {
/**
* @description: 查询获取每个账户所属的用户信息
* 多表查询
* @date: 2022-01-23 16:28
* @return
*/
@Select("select * from account")
@Results(value = {
@Result(id = true,column = "id",property = "id"),
@Result(column = "uid",property = "uid"),
@Result(column = "money",property = "money"),
@Result(property = "user" ,column = "uid" ,one=@One(select = "com.mybatisD.dao.IUserDao.findById" ,fetchType = FetchType.EAGER))
})
List<Account> findAll();
/**
* @description: 根据用户id查询账户信息
* @date: 2022-01-23 18:14
* @param
* @return
*/
@Select("select * from account where uid = #{id}")
List<Account> findAccountByUid(Integer id);
}
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>
<!--配置properties文件 可以在标签内部配置数据库的信息,也可以通过属性引用外部配置信息
resource属性:用于指定配置文件的位置,是按照类路径的写法来写平且必须存在与类路径下
-->
<properties resource="jdbc.properties">
</properties>
<!--开启二级缓存-->
<typeAliases>
<package name="com.mybatisD.daomain"/>
</typeAliases>
<!--配置环境-->
<environments default="mysql">
<!--配置mysql环境-->
<environment id="mysql">
<!--配置事物类型-->
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<!--配置连接数据库的4个信息-->
<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>
<!--指定映射配置文件的位置,映射配置文件指的是每个到独立的配置文件-->
<!--<mappers>
<mapper resource="com/mybatisD/dao/IUserDao.xml"/>
<mapper resource="com/mybatisD/dao/IAccountDao.xml"/>
</mappers>-->
<!--如果要是用注解来配置的话,此处应该使用class属性指定被注解的dao全限定类名-->
<!--<mappers>
<mapper class="com.mybatisD.dao.IUserDao"/>
</mappers>-->
<!--配置别名-->
<!--指定带有注解的dao接口所在位置-->
<mappers>
<!--package标签用于指定doa接口所在的包,当指定了之后就不需要在写mapper以及resource或者class了-->
<package name="com.mybatisD.dao"/>
</mappers>
</configuration>
jdbc.properties数据连接
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/newsql?characterEncoding=utf-8
jdbc.username=root
jdbc.password=
日志信息配置log4j.properties
log4j.rootCategory=debug,CONSOLE, LOGFILE
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE
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
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
User测试实现类
package com.mybateisT.TestD;
import com.mybatisD.dao.IUserDao;
import com.mybatisD.daomain.User;
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.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
/**
* @author :程序员徐大大
* @description:TODO
* @date :2022-01-23 14:53
*/
public class MybatisAnnoTest {
/**
* @author: 测试基于注解的mybatis使用
* @description: TODO
* @date: 2022-01-23 14:50
* @return
*/
private InputStream in;
private SqlSessionFactory factory;
private SqlSession session;
private IUserDao userDao;
@Before
public void init() throws IOException {
//1.获取字节输入流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.根据字节输入流构建SqlSessionFactory
factory = new SqlSessionFactoryBuilder().build(in);
//3.根据SqlSessionFactory生产一个SqlSession
session = factory.openSession();
//4.使用Selsession获取Dao代理对象
userDao = session.getMapper(IUserDao.class);
}
@After
public void destory() throws IOException {
session.commit();
session.close();
in.close();
}
@Test
public void TestFindAll() throws IOException {
//5.执行Dao方法
List<User> users = userDao.findAll();
for (User user: users) {
System.out.println(user);
}
}
@Test
public void TestSave() {
User user = new User();
user.setUsername("test to annotation");
user.setAddress("北极");
user.setSex("女");
user.setBirthday(new Date());
userDao.saveUser(user);
System.out.println(user);
}
@Test
public void TestUpdate() {
User user = new User();
user.setId(3);
user.setUsername("mybatis to annotation update");
user.setAddress("北极");
user.setSex("nv");
user.setBirthday(new Date());
userDao.updateUser(user);
System.out.println(user);
}
@Test
public void TestDelete() {
List<User> users = userDao.findAll();
userDao.deleteUser(12);
for (User user:users) {
System.out.println(user);
}
}
@Test
public void TestFindOne() {
User users = userDao.findById(1);
System.out.println(users);
}
@Test
public void TestFindName() {
List<User> users = userDao.findUserByName("%赵%");
for (User user: users) {
System.out.println(users);
}
}
@Test
public void TestTotal() {
int total = userDao.findTotalUser();
System.out.println(total);
}
@Test
public void testFindAccount(){
List<User> users = userDao.findAllAccount();
for (User user:users) {
System.out.println("-----用户信息------");
System.out.println(user);
System.out.println(user.getAccounts());
}
}
}
Account测试实现类
package com.mybateisT.TestD;
import com.mybatisD.dao.IAccount;
import com.mybatisD.dao.IUserDao;
import com.mybatisD.daomain.Account;
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.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
/**
* @author :程序员徐大大
* @description:TODO
* @date :2022-01-23 17:39
*/
public class AccountTest {
private InputStream in;
private SqlSessionFactory factory;
private SqlSession session;
private IAccount iAccount;
@Before
public void init() throws IOException {
//1.获取字节输入流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.根据字节输入流构建SqlSessionFactory
factory = new SqlSessionFactoryBuilder().build(in);
//3.根据SqlSessionFactory生产一个SqlSession
session = factory.openSession();
//4.使用Selsession获取Dao代理对象
iAccount = session.getMapper(IAccount.class);
}
@After
public void destory() throws IOException {
session.commit();
session.close();
in.close();
}
/**
* @author: 程序员小徐同学
* @description: 查询所有账户,并且获取每个账户所属用户信息
* @date: 2022-01-23 17:40
* @param
* @return
*/
@Test
public void testFindAllAccount(){
List<Account> accounts = iAccount.findAll();
for (Account account:accounts) {
System.out.println("--------每个账户信息---------");
System.out.println(account);
System.out.println(account.getUser());
}
}
}
二级缓存测试实现类
package com.mybateisT.TestD;
import com.mybatisD.dao.IUserDao;
import com.mybatisD.daomain.User;
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.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
/**
* @author :程序员徐大大
* @description:测试二级缓存
* @date :2022-01-23 18:34
*/
public class SecondLevelCatchTest {
/**
* @author: 测试二级缓存
* @description: TODO
* @date: 2022-01-23 14:50
* @return
*/
private InputStream in;
private SqlSessionFactory factory;
private SqlSession session;
private IUserDao userDao;
@Before
public void init() throws IOException {
//1.获取字节输入流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.根据字节输入流构建SqlSessionFactory
factory = new SqlSessionFactoryBuilder().build(in);
//3.根据SqlSessionFactory生产一个SqlSession
session = factory.openSession();
//4.使用Selsession获取Dao代理对象
userDao = session.getMapper(IUserDao.class);
}
@After
public void destory() throws IOException {
in.close();
}
@Test
public void TestFindOne() throws IOException {
//5.执行Dao方法
User user = userDao.findById(1);
System.out.println(user);
session.close();//释放一级缓存
SqlSession session1 = factory.openSession();//再次打开session
IUserDao userDao1 = session1.getMapper(IUserDao.class);
User user1 = userDao1.findById(1);
System.out.println(user1);
System.out.println(user==user1);
}
}
注解开发配置二级缓存现在SqlMapConfig.xml先配置
```xml
<!--开启二级缓存-->
<typeAliases>
<package name="com.mybatisD.daomain"/>
</typeAliases>
在IUserDao接口类加入注解@CacheNamespace开启二级缓存
/**
* @author: 徐大大
* @description: TODO
* @date: 2022-01-23 14:50
* 在mybatis中针对CRUD一共有四个注解
* @Select @Insert @Update @Delete
*/
@CacheNamespace(blocking = true) //注解开启二级缓存,要在SqlMapConfig.xml里面开启二级缓存
public interface IUserDao {
编写测类
package com.mybateisT.TestD;
import com.mybatisD.dao.IUserDao;
import com.mybatisD.daomain.User;
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.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
/**
* @author :程序员徐大大
* @description:测试二级缓存
* @date :2022-01-23 18:34
*/
public class SecondLevelCatchTest {
/**
* @author: 测试二级缓存
* @description: TODO
* @date: 2022-01-23 14:50
* @return
*/
private InputStream in;
private SqlSessionFactory factory;
private SqlSession session;
private IUserDao userDao;
@Before
public void init() throws IOException {
//1.获取字节输入流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.根据字节输入流构建SqlSessionFactory
factory = new SqlSessionFactoryBuilder().build(in);
//3.根据SqlSessionFactory生产一个SqlSession
session = factory.openSession();
//4.使用Selsession获取Dao代理对象
userDao = session.getMapper(IUserDao.class);
}
@After
public void destory() throws IOException {
in.close();
}
@Test
public void TestFindOne() throws IOException {
//5.执行Dao方法
User user = userDao.findById(1);
System.out.println(user);
session.close();//释放一级缓存
SqlSession session1 = factory.openSession();//再次打开session
IUserDao userDao1 = session1.getMapper(IUserDao.class);
User user1 = userDao1.findById(1);
System.out.println(user1);
System.out.println(user==user1);
}
}
Resources 类,顾名思义就是资源,用于读取资源文件。其有很多方法通过加载并解析资源文件,返回不同类型的 IO 流对象。
SqlSessionFactoryBuilder
用来创建 SqlSessionFactory 的创造者,在整个项目中,只会使用一次,就是用来创建 SqlSessionFactory 的。用后即丢
SqlSessionFactory
创建 sqlSession 的单例工厂。在整个项目中,应该只有一个 SqlSessionFactory 的单例
SqlSession
一个 SqlSession 对应着一次数据库会话,一次会话以SqlSession 对象的创建开始,以 SqlSession 对象的关闭结束。
SqlSession 接口对象是线程不安全的,所以每次数据库会话结束前,需要马上调用其 close()方法,将其关闭。再次需要会话,再次创建。
改进 MybatisUtil
在 快速开始 栏目中,我们将获取 Mybatis 的 sqlSession 对象的过程,封装成了一个工具类
但是,sqlSession 并没有作为一个成员变量,存储在 MybatisUtil 中,这样,我们对 sqlSession 执行 close ,必须要手动去调用我们外部获得的 sqlSession ,其实,我们可以将 sqlSession 变成一个成员变量,然后,在 MybatisUtil 中,写一个close 方法。
但是,我们要注意,sqlSession 是线程不安全的,每次使用完后,我们都需要进行 close,然后,在下次使用的时候,再次连接 sqlSession ,如果把 sqlSession ,作为一个静态成员变量,放在 MybatisUtil 中,势必会引发线程相关的问题。
为了解决这个问题,我们需要来介绍一下 ThreadLocal
1、 ThreadLocal
2、 使用 ThreadLocal 来改写