1、第一个Mybatis程序
思路:搭建环境->导入Mybatis->编写代码->测试
1.1搭建环境
搭建数据库
create database `Mybatis`;
use `mybatis`;
create table `user`(
id int not null,
name varchar(30) default null,
pwd varchar(30) default null,
primary key(`id`)
) ENGINE=INNODB default CHARSET=utf8;
insert into `user`(`id`, `name`, `pwd`)
values(1, 'qwer', '123456'),
(2, 'asdf', '123456'),
(3, 'zxcv', '123456');
新建项目
- 1.新建一个普通的maven项目
- 2.删除src目录
- 3.导入maven依赖
mysql
mysql-connector-java
5.1.46
org.mybatis
mybatis
3.5.2
junit
junit
4.12
1.2创建一个模块
- 编写mybatis 核心文件
- 编写mybatis 工具类
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static{//初始加载
try {
//获取SqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//有了 SqlSessionFactory,可以从中获得 SqlSession 的实例。
// SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。openSession
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
1.3 编写代码
- 实现类
public class User {
private int id;
private String name;
private String pwd;
public User() {
}
public User(int id, String name, String pwd) {
this.id = id;
this.name = name;
this.pwd = pwd;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
}
- dao接口
public interface UserDao {
List getUserList();
}
- 接口实现 由DaoImpl转换为了Mapper
1.4测试
Junit测试
public class UserDemoTest {
@Test
public void test(){
//获得sqlsession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
//执行sql
UserDao userDao = sqlSession.getMapper(UserDao.class);
List userList = userDao.getUserList();
for (User user :userList) {
System.out.println(user);
}
//关闭sqlsession
sqlSession.close();
}
}
注意点:
错误一:org.apache.ibatis.binding.BindingException: Type interface com.beibei.dao.UserDao is not known to the MapperRegistry.
MapperRegistry是什么?
解决方法:mybatis配置文件中添加
错误二:
java.lang.ExceptionInInitializerError
Could not find resource com.beibei.dao.UserMapper.xml
解决方法:内部pom.xml添加
src/main/resources
**/*.properties
**/*.xml
true
src/main/java
**/*.properties
**/*.xml
true
生命周期和作用域
SqlSessionFactoryBuilder->sqlSessionFactory->SqlSession
SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量):不占用过多资源
SqlSessionFactory 最佳作用域是应用作用域:等同于连接池过多创建影响性能,单例模式
SqlSession 实例不是线程安全的,将 SqlSession 放在一个和 HTTP 请求相似的作用域中,每次收到 HTTP 请求,就可以打开一个SqlSession,返回一个响应后,就关闭它。sqlSession.close();
2. CRUD
1.namespace
namespace中的包名要和Dao/mapper接口的包名保持一致。
2.select/insert/update/delete
选择,查询语句:
- id:对应namespace方法名;
- resultType:Sql语句执行返回值!
- parameterType:参数类型
1.编写接口
public interface UserDao {
List getUserList();
User getUserByID(int id);
int addUser(User user);
int updateUser(User user);
int deleteUser(int id);
}
2.编写mapper中的sql语句
insert into mybatis.user(id, name, pwd) values (#{id}, #{name}, #{pwd});
update mybatis.user
set name=#{name},
pwd=#{pwd}
where id=#{id};
delete from mybatis.user where id = #{id}
3.测试 ==注意事务的提交==
@Test
public void testSelectbyID(){
//获得sqlsession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
//执行sql
UserDao userDao = sqlSession.getMapper(UserDao.class);
User user = userDao.getUserByID(1);
System.out.println(user);
//关闭sqlsession
sqlSession.close();
}
@Test
public void testAddUser(){
//获得sqlsession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
//执行sql
UserDao userDao = sqlSession.getMapper(UserDao.class);
int res = userDao.addUser(new User(4, "hanhan", "123456"));
System.out.println(res);
//提交事务
sqlSession.commit();
//关闭sqlsession
sqlSession.close();
}
@Test
public void testUpdateUser(){
//获得sqlsession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
//执行sql
UserDao userDao = sqlSession.getMapper(UserDao.class);
int res = userDao.updateUser(new User(4, "hehe", "123123"));
System.out.println(res);
//提交事务
sqlSession.commit();
//关闭sqlsession
sqlSession.close();
}
@Test
public void testDeleteUser(){
//获得sqlsession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
//执行sql
UserDao userDao = sqlSession.getMapper(UserDao.class);
int res = userDao.deleteUser(4);
System.out.println(res);
//提交事务
sqlSession.commit();
//关闭sqlsession
sqlSession.close();
}
4.Map妙用
对于字段过多的情况,由于pojo对象的构造方法难以分属性创建构造方法,在全属性构造方法下就必须传所有参数,可以使用Map规避这个问题,只需传指定参数即可。
dao:
int updateUserByMap(Map map);
daomapper:
update mybatis.user
set name=#{username}
where id=#{id};
test:
@Test
public void testUpdateUserByMap(){
//获得sqlsession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao User = sqlSession.getMapper(UserDao.class);
Map map = new HashMap();
map.put("id", 4);
map.put("username", "meng");
int i = User.updateUserByMap(map);
System.out.println(i);
sqlSession.commit();
sqlSession.close();
}
5.模糊查询
1.Java代码执行的时候,传递通配符%%
List userlist = mapper.getUserLike("%李%");
2.在sql中拼接使用通配符
select * from mybatis.user where name like "%"#{value}"%";
3.配置解析
1.核心配置文件
mybatis-config.xml
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
- environments(环境配置)
尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
- 默认使用的环境 ID(比如:default="development")。
- 每个 environment 元素定义的环境 ID(比如:id="development")。
- 事务管理器的配置(比如:type="JDBC")。
- 存在JDBC和MANAGED 两者,MANAGED一般不用
- 数据源的配置(比如:type="POOLED")
- UNPOOLED 无连接池,性能依赖于数据库
- POOLED 存在连接池,减少连接响应时间,可进一步配置
- JNDI EJB 或应用服务器这类容器中使用
2.properties(属性)
通过properties来实现配置文件引用
编写db.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/Mybatis?useSSL=true&useUnicode=true&charterEncoding=UTF-8
username=root
password=root123
mybatis-config.xml中进行引用
3.类型别名(typeAliases)
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。
若有注解,则别名为其注解值。
@Alias("author")
public class Author {
...
}
4.settings(设置)
核心配置:
cacheEnabled:全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。默认true
lazyLoadingEnabled: 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。默认false
logImpl:指定 MyBatis 所用日志的具体实现,未指定时将自动查找。
完整的配置元素:
5.其他配置,基本使用不到
- 类型处理器(typeHandlers)
- objectFactory(对象工厂)
- plugins(插件)
6.其他配置,基本使用不到
MapperRegistry:注册绑定Mapper文件
方式一:
方式二:使用class文件绑定注册
注意点:
接口和Mapper.xml文件名称必须一致
接口和Mapper.xml文件必须在一个包下
方式三:通过包引入
注意点:
接口和Mapper.xml文件名称必须一致
接口和Mapper.xml文件必须在一个包下
7.ResultMap结果映射集
当pojo对象与数据库对象字段名称不相同时,无法正确查询结果,此时需要使用ResultMap 进行映射
不进行resultType返回,使用resultMap进行对象返回
property是数据库字段名
column是pojo属性
4.日志
如果数据库操作出现异常,需要通过日志进行更方便的排错
在mybatis-config.xml中存在settings进行日志配置
logImpl 指定 MyBatis所用日志的具体实现,未指定时将自动查找。
核心2种,STDOUT_LOGGING 和常用的 LOG4J
4.1日志工厂
STDOUT_LOGGING标准的日志工厂实现,无需添加外部包,配置好可直接使用
配置项:
输出结果:
Opening JDBC Connection
Created connection 156545103.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@954b04f]
==> Preparing: select * from mybatis.user where id = ?;
==> Parameters: 1(Integer)
<== Columns: id, name, pwd
<== Row: 1, qwer, 123456
<== Total: 1
User{id=1, name='qwer', password='123456'}
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@954b04f]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@954b04f]
Returned connection 156545103 to pool.
4.2log4j
优点:
- 可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码
- 日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等
- 导入log4j
log4j
log4j
1.2.17
- resources下编写log4j.properties
当前配置为输出到文件和控制台
### 配置根 ###
#日志信息输出到console和file下
log4j.rootLogger = debug,console,file
### 设置输出sql的级别,其中logger后面的内容全部为jar包中所包含的包名 ###
log4j.logger.org.apache=dubug
log4j.logger.java.sql.Connection=dubug
log4j.logger.java.sql.Statement=dubug
log4j.logger.java.sql.PreparedStatement=dubug
log4j.logger.java.sql.ResultSet=dubug
### 配置输出到控制台 ###
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern = %d{ABSOLUTE} %5p %c{1}:%L - %m%n
### 配置输出到文件 ###
log4j.appender.file = org.apache.log4j.FileAppender
log4j.appender.file.File = logs/log.log
log4j.appender.file.Append = true
log4j.appender.file.Threshold = DEBUG
log4j.appender.file.layout = org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
3.配置log4j为mybatis日志实现
4.测试运行
19:31:52,373 DEBUG LogFactory:105 - Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.
19:31:52,376 DEBUG LogFactory:105 - Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.
19:31:52,414 DEBUG PooledDataSource:353 - PooledDataSource forcefully closed/removed all connections.
19:31:52,414 DEBUG PooledDataSource:353 - PooledDataSource forcefully closed/removed all connections.
19:31:52,415 DEBUG PooledDataSource:353 - PooledDataSource forcefully closed/removed all connections.
19:31:52,415 DEBUG PooledDataSource:353 - PooledDataSource forcefully closed/removed all connections.
19:31:52,563 DEBUG JdbcTransaction:136 - Opening JDBC Connection
19:31:52,868 DEBUG PooledDataSource:424 - Created connection 793315160.
19:31:52,868 DEBUG JdbcTransaction:100 - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@2f490758]
19:31:52,875 DEBUG getUserList:143 - ==> Preparing: select * from mybatis.user;
19:31:52,920 DEBUG getUserList:143 - ==> Parameters:
19:31:52,952 DEBUG getUserList:143 - <== Total: 4
User{id=1, name='qwer', password='123456'}
User{id=2, name='asdf', password='123456'}
User{id=3, name='zxcv', password='123456'}
User{id=4, name='meng', password='123123'}
19:31:52,955 DEBUG JdbcTransaction:122 - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@2f490758]
19:31:52,961 DEBUG JdbcTransaction:90 - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@2f490758]
19:31:52,961 DEBUG PooledDataSource:381 - Returned connection 793315160 to pool.
5.log4j项目使用
使用类中引入
import org.apache.log4j.Logger;
具体测试使用
static Logger logger = Logger.getLogger(UserDemoTest.class);
@Test
public void testLog4j(){
logger.info("info: testLog4j");
logger.debug("debug: testLog4j");
logger.error("error: testLog4j");
}
可以在输出文件logs.log看差异
[ INFO ] - 2020-08-30 19:46:07 info: testLog4j
[ DEBUG ] - 2020-08-30 19:46:07 debug: testLog4j
[ ERROR ] - 2020-08-30 19:46:07 error: testLog4j
5.分页实现
为什么需要分页?
- 减少数据处理量
5.1 limit 分页
limit语法:
mysql> SELECT * FROM table LIMIT 5,10; // 检索记录行 6-15
//为了检索从某一个偏移量到记录集的结束所有的记录行,可以指定第二个参数为 -1:
mysql> SELECT * FROM table LIMIT 95,-1; // 检索记录行 96-last.
//如果只给定一个参数,它表示返回最大的记录行数目:
mysql> SELECT * FROM table LIMIT 5; //检索前 5 个记录行
//换句话说,LIMIT n 等价于 LIMIT 0,n。
测试:
创建接口:
List getUserListByLimit(Map map);
mapper:
测试调用:
@Test
public void testSelectByLimit(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao userdao = sqlSession.getMapper(UserDao.class);
Map map = new HashMap();
map.put("startIndex", 0);
map.put("pageSize", 2);
List userList = userdao.getUserListByLimit(map);
for (User user:userList) {
System.out.println(user);
}
sqlSession.close();
}
5.2.RowBounds 分页
不再使用sql实现分页,通过mybatis官方方法实现(不推荐)
接口:
List getUserListByRowBounds();
mapper.xml
测试脚本
@Test
public void testSelectByRowBounds(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
RowBounds rowBounds = new RowBounds(1,2 );
List
for (User user: userList) {
System.out.println(user);
}
sqlSession.close();
}
5.3 其他插件方式
Mysql分页处理(PageHelper)
6 使用注解开发
**面向接口编程 **
- 解耦
- 定义(规范,约束)与实现(名实分离的原则)的分离。
- 接口的本身反映了系统设计人员对系统的抽象理解
6.1使用注解开发
1.注解在接口上实现
@Select("select * from mybatis.user")
List getUsers();
2.需要在核心配置中绑定接口
3.测试
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
List userList = mapper.getUsers();
for (User user: userList ) {
System.out.println(user);
}
sqlSession.close();
}
本质:反射来获取注解信息实现
底层:动态代理
6.2 注解开发CRUD
6.2.1 自动提交事务
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession(true);//自动提交事务
}
6.2.2 编写接口
@Insert("insert into mybatis.user(id, name, pwd) values(#{id}, #{name}, #{pwd})")
int addUser(@Param("id") int id, @Param("name") String name, @Param("pwd") String password);
@Update("update mybatis.user set name=#{name} where id = #{id}")
int updateUser(@Param("id") int id, @Param("name") String name);
6.2.3 增加测试类
@Test
public void testAddUser(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
int i = mapper.addUser(5, "qwer", "123321");
System.out.println(i);
}
@Test
public void testUpdateUser(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
int i = mapper.updateUser(5, "asdf");
System.out.println(i);
}
6.3 #{} 和${} 区别
{}是预编译处理,对应JBDC中的PreparedStatement,可以防止依赖注入。
${}是字符串替换。不能防止依赖注入
7.Lombok
不用再getter setter 构造 toString ,注解即可完成
1.idea安装插件
2.导入依赖
org.projectlombok
lombok
1.18.12
provided
3.使用:
@ToString
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
public class User {
private int id;
private String name;
private int age;
private String birth;
private Date birthday;
}
@Data:生成无参 get set toString equeals hashcode
@AllArgsConstructor:生成全参
@NoArgsConstructor:生成无参
7 复杂查询
7.1环境搭建
1.导入lombok
2.新建实体类Teacher、Student
3.建立Mapper接口
4.建立Mapper.xml文件
5.核心配置文件中绑定注册的Mapper接口或文件
6.测试查询是否成功
7.2 多对一,多表查询
方式一:按照查询嵌套处理
核心mapper配置如下
方式二:按结果嵌套查询
7.3 一对多,集合查询
8 动态sql
- if
- choose (when, otherwise)
- trim (where, set)
- foreach
数据库搭建
CREATE TABLE `blog`(
`id` VARCHAR(50) NOT NULL COMMENT '博客id',
`title` VARCHAR(100) NOT NULL COMMENT '博客标题',
`author` VARCHAR(30) NOT NULL COMMENT '博客作者',
`create_time` DATETIME NOT NULL COMMENT '创建时间',
`views` INT(30) NOT NULL COMMENT '浏览量',
primary key(`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8;
pojo对象
@Data
public class Blog {
private String id;
private String title;
private String author;
private Date createTime;
private int views;
}
if判断动态语句
mapper
insert into mybatis.blog(id, title, author, create_time, views)
values (#{id}, #{title}, #{author}, #{createTime}, #{views});
测试
@org.junit.Test
public void testAddBlog(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Blog blog = new Blog();
blog.setAuthor("QWER");
blog.setCreateTime(new Date());
blog.setTitle("QWEQWRT");
blog.setViews(6213);
blog.setId(IDutils.getIdrandom());
int i = mapper.addBlog(blog);
System.out.println(i);
sqlSession.commit();
sqlSession.close();
}
@org.junit.Test
public void testQueryBlogIF(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Map map = new HashMap();
map.put("author","QWER");
List blogs = mapper.queryBlogIF(map);
for (Blog blog : blogs) {
System.out.println(blog.toString());
}
// sqlSession.commit();
sqlSession.close();
}
choose(when otherwise)选择动态语句
和java的switch效果相同
mapper:
test:
@org.junit.Test
public void testQueryBlogChoose(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Map map = new HashMap();
map.put("author","QWER");
map.put("title", "QWEQWRT");
List blogs = mapper.queryBlogIF(map);
for (Blog blog : blogs) {
System.out.println(blog.toString());
}
// sqlSession.commit();
sqlSession.close();
}
trim (where, set)
where 是取消where 1=1 使用的,update中可能使用
trim 则是优化多于的and or ,等元素的
==set== 有使用的必要,优化update中可能存在的多于","
进行set相关的测试
update mybatis.blog
author = #{author},
views = views+1000,
where 1= 1 and author = "pgm"
测试:
@org.junit.Test
public void testUpdateBlogSet(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Map map = new HashMap();
map.put("author","darmon");
map.put("title", "QWEQWRT");
int i= mapper.updateBlogSet(map);
System.out.println(i);
sqlSession.commit();
sqlSession.close();
}
foreach 集合类条件动态语句
你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。==当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值==
mapper
测试用例
@org.junit.Test
public void testQueryBlogIn(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Map map = new HashMap();
ArrayList authors = new ArrayList();
authors.add("pgm");
authors.add("darmon");
map.put("authors",authors);
List blogs = mapper.queryBlogIn(map);
for (Blog blog : blogs) {
System.out.println(blog.toString());
}
sqlSession.commit();
sqlSession.close();
}
9.缓存机制
为了提升查询效率,引入了缓存机制
mybatis缓存共定义了2级缓存
一级缓存(本地缓存):
- 默认情况开启
- 作用于Sqlsession级别(sqlsession.close关闭便关闭)
- 修改数据后缓存失效(全刷新,失效)
- sqlsession.clearCache();//清理缓存
二级缓存:
- namespace级别缓存(又叫mapper级别,任意访问mapper.xml的文件都能使用)
- 主配置文件中必须开启缓存设置(默认开启)
- 在mapper.xml中添加
既开启二级缓存 - 缓存设置(FIFO 缓存(先进先出),每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的)
- 默认使用LRU算法机制(最近最少使用)
- 缓存策略包含
LRU – 最近最少使用:移除最长时间不被使用的对象。
FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。
WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象
存在多级缓存时,查询调用顺序是== 优先二级缓存 ==,==其次一级缓存==,==最后查数据库==
Mybatis执行流程