Mybatis
1、简介
1.1、什么是mybatis
-
MyBatis 是一款优秀的持久层框架
-
它支持自定义 SQL、存储过程以及高级映射。
-
MyBatis 免除了几乎所有的 JDBC 设置参数和获取结果集的工作。
-
MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录
-
MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。
-
2013年11月迁移到Github。
如何获得mybatis
-
maven仓库
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.5</version> </dependency>
1.2、持久层
数据持久化
-
持久化 就是程序的数据在持久状态和瞬时状态转化的过程
-
内存:断电即失
-
数据库(jdbc),io文件持久化
-
生活:冷藏
为什么 要持久化
- 有些对象不能丢掉
- 内存贵
1.4、为什么要mybatis
- 方便
- 传统jdbc复杂。简化框架
- 优点
- 简单易学:本身就很小且简单。
- 灵活
- 解除sql与程序代码的耦合。
- 提供映射标签,支持对象与数据库的orm字段关系映射
- 提供对象关系映射标签,支持对象关系组建维护
- 提供xml标签,支持编写动态sql。
2、第一个mybatis程序
环境–>导入mybaitis–>编写代码–>测试
2.1、导入依赖(父项目)
<dependencies>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</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.18.12</version>
</dependency>
</dependencies>
2.2、创建模块
-
编写mybatis配置文件
<?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> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/db_test?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> <!--每个mapper都需要有注册--> <mappers> <mapper resource="com/liu/dao/UserMapper.xml"/> </mappers> </configuration>
-
编写工具类
//SqlSessionFactory -->SqlSession public class MybatisUtils { private static SqlSessionFactory sqlSessionFactory; static { try { //1.获取sqlSession String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } //2.拿到sqlSession就拿到操作数据库语句的所有方法 public static SqlSession getSqlSession(){ return sqlSessionFactory.openSession(); }
2.3、编写代码
-
实体类
-
@Data @AllArgsConstructor @NoArgsConstructor public class User { private int id; private String username; private String password; }
-
dao接口
public interface UserDao { List<User> getList(); }
-
接口实现类
<?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==>绑定指定对应dao、mapper接口--> <mapper namespace="com.liu.dao.UserDao"> <select id="getList" resultType="com.liu.pojo.User"> select * from db_test.t_user </select> </mapper>
2.4、测试
@Test
public void test() {
//1.获取sqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
//2.执行sql (方式一)
UserDao mapper = sqlSession.getMapper(UserDao.class);
List<User> list = mapper.getList();
for (User user : list) {
System.out.println("方式1:"+user);
}
//方式二
List<User> list1 = sqlSession.selectList("com.liu.dao.UserDao.getList");
for (User user : list1) {
System.out.println("方式2:"+user);
}
sqlSession.close();
}
2.5、解决xml打包不了的问题
在pom文件加入打包过滤
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
3、CRUD
namespace中的包名要和dao/mapper接口包名一致
4、配置解析
1、核心配置文件
- mybatis-config.xml
- MyBatis配置包含对MyBatis的行为产生巨大影响的设置和属性
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
2、environments(环境配置)
MyBatis可以配置多个环境
不过,要记住的一件事很重要:虽然可以配置多个环境,但每个SqlSessionFactory实例只能选择一个。
mybatis默认的事务管理器是jdbc,连接池:POOLED
3、properties(属性)
通过properties来引用配置文件
这些是可外部化的,可替换的属性,可以在典型的Java Properties文件实例中对其进行配置,或者通过properties元素的子元素传递【db.properties】
3.1、编写db.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/db_test?useUnicode=true&characterEncoding=utf-8&useSSL=true
username=root
password=root
3.2、核心配置引入
<properties resource="db.properties">
<property name="username" value="dev_user"/>
<property name="password" value="F2Fa3!33TYyg"/>
</properties>
- 可以在其中增加属性设置
- 如果文件有同一个字段,优先使用的是外部配置文件的
4、typeAliases(类型别名)
-
类型别名只是Java类型的简称
-
为了减少完全限定的类名的冗余类型而存在
<!--别名--> <typeAliases> <typeAlias type="com.liu.pojo.User" alias="User"/> </typeAliases>
-
还可以指定MyBatis将在其中搜索bean的程序包(默认bean类首字母小写)
<!--别名-->
<typeAliases>
<package name="com.liu.pojo"/>
</typeAliases>
5、settings(设置)
这些是极其重要的调整,可修改MyBatis在运行时的行为方式。
6、其他配置
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins(插件)
7、mappers(映射器)
MapperRegistry:注册绑定我们的Mapper
方式1:【推荐使用】
<!--每个mapper都需要有注册-->
<mappers>
<mapper resource="com/liu/dao/UserMapper.xml"/>
</mappers>
方式2:使用class绑定
<!--每个mapper都需要有注册-->
<mappers>
<mapper class="com.liu.dao.UserMapper"/>(UserMapper->UserDao就行不通)
</mappers>
- 接口和他的Mapper配置文件必须同名
- 接口和他的Mapper配置文件必须同一个包下
方式3:包扫描绑定
<!--每个mapper都需要有注册-->
<mappers>
<package name="com.liu.dao"/>(UserMapper->UserDao就行不通)
</mappers>
- 接口和他的Mapper配置文件必须同名
- 接口和他的Mapper配置文件必须同一个包下
5、生命和生命周期
范围和生命周期类非常重要。错误使用它们会导致严重的并发问题
SqlSessionFactoryBuilder:
- 创建SqlSessionFactory后,无需保留它
- 局部变量
SqlSessionFactory
- 和数据库连接池很相似
- SqlSessionFactory应该在应用程序执行期间存在。没有理由处置或重新创建它
- 因此,SqlSessionFactory的最佳范围是应用程序范围
- 最简单的是使用Singleton模式或Static Singleton模式。
SqlSession
- 每个线程应具有自己的SqlSession实例(连接池的一个请求)
- SqlSession的实例不应该共享,也不是线程安全的
- 用完关闭,否则占用资源
每一个Mapper就是一个业务、一个实例
6、resultMap
结果集映射
id username password
id username pwd
<!--结果集映射-->
<resultMap id="UserMapper" type="User">
<!--column:数据库中的字段名,property实体类中的字段名-->
<result column="id" property="id"/>
<result column="username" property="username"/>
<result column="password" property="pwd"/>
</resultMap>
7、日志
7.1、日志工厂
- SLF4J
- LOG4J
- LOG4J2
- JDK_LOGGING
- COMMONS_LOGGING
- STDOUT_LOGGING
- NO_LOGGING
STDOUT_LOGGING:标准日志
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
7.2、Log4j
- Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件
- 我们也可以控制每一条日志的输出格式
- 通过定义每一条日志信息的级别
- 通过一个配置文件来灵活地进行配置
1、导包
<!-- log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
2、log4j.properties
### 配置根 ###
log4j.rootLogger = debug,console ,fileAppender,dailyRollingFile,ROLLING_FILE,MAIL,DATABASE
### 设置输出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.fileAppender = org.apache.log4j.FileAppender
log4j.appender.fileAppender.File = logs/log.log
log4j.appender.fileAppender.Append = true
log4j.appender.fileAppender.Threshold = DEBUG
log4j.appender.fileAppender.layout = org.apache.log4j.PatternLayout
log4j.appender.fileAppender.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
### 配置输出到文件,并且每天都创建一个文件 ###
#log4j.appender.dailyRollingFile = org.apache.log4j.DailyRollingFileAppender
#log4j.appender.dailyRollingFile.File = logs/log.log
#log4j.appender.dailyRollingFile.Append = true
#log4j.appender.dailyRollingFile.Threshold = DEBUG
#log4j.appender.dailyRollingFile.layout = org.apache.log4j.PatternLayout
#log4j.appender.dailyRollingFile.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
### 配置输出到文件,且大小到达指定尺寸的时候产生一个新的文件 ###
#log4j.appender.ROLLING_FILE=org.apache.log4j.RollingFileAppender
#log4j.appender.ROLLING_FILE.Threshold=ERROR
#log4j.appender.ROLLING_FILE.File=rolling.log
#log4j.appender.ROLLING_FILE.Append=true
#log4j.appender.ROLLING_FILE.MaxFileSize=10KB
#log4j.appender.ROLLING_FILE.MaxBackupIndex=1
#log4j.appender.ROLLING_FILE.layout=org.apache.log4j.PatternLayout
#log4j.appender.ROLLING_FILE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n
### 配置输出到邮件 ###
#log4j.appender.MAIL=org.apache.log4j.net.SMTPAppender
#log4j.appender.MAIL.Threshold=FATAL
#log4j.appender.MAIL.BufferSize=10
#log4j.appender.MAIL.From=chenyl@yeqiangwei.com
#log4j.appender.MAIL.SMTPHost=mail.hollycrm.com
#log4j.appender.MAIL.Subject=Log4J Message
#log4j.appender.MAIL.To=chenyl@yeqiangwei.com
#log4j.appender.MAIL.layout=org.apache.log4j.PatternLayout
#log4j.appender.MAIL.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n
### 配置输出到数据库 ###
#log4j.appender.DATABASE=org.apache.log4j.jdbc.JDBCAppender
#log4j.appender.DATABASE.URL=jdbc:mysql://localhost:3306/db_test
#log4j.appender.DATABASE.driver=com.mysql.jdbc.Driver
#log4j.appender.DATABASE.user=root
#log4j.appender.DATABASE.password=root
#log4j.appender.DATABASE.sql=INSERT INTO LOG4J (Message) VALUES ('[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n')
#log4j.appender.DATABASE.layout=org.apache.log4j.PatternLayout
#log4j.appender.DATABASE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n
#log4j.appender.A1=org.apache.log4j.DailyRollingFileAppender
#log4j.appender.A1.File=SampleMessages.log4j
#log4j.appender.A1.DatePattern=yyyyMMdd-HH'.log4j'
#log4j.appender.A1.layout=org.apache.log4j.xml.XMLLayout
3、设置config配置文件
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
4、简单测试
public class UserDaoTest {
static Logger logger = Logger.getLogger(UserDaoTest.class);
@Test
public void log4j() {
logger.info("info开始log4j");
logger.debug("debug开始log4j");
logger.error("error开始log4j");
}
}
8、分页
- 减少数据处理量
8.1、Limit分页
1、接口
public interface UserMapper {
List<User> getPage(Map<String,Integer> map);
}
2、mapper.xml
<!--分页-->
<select id="getPage" parameterType="map" resultMap="UserMapper">
select * from db_test.t_user limit #{startIndex},#{pageSize};
</select>
<!--结果集映射-->
<resultMap id="UserMapper" type="User">
<!--column:数据库中的字段名,property实体类中的字段名-->
<result column="id" property="id"/>
<result column="username" property="username"/>
<result column="password" property="pwd"/>
</resultMap>
3、测试
@Test
public void getPage() {
//1.获取sqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
//2.执行sql (方式一)
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("startIndex",0);
map.put("pageSize",2);
List<User> users = mapper.getPage(map);
for (User user : users) {
System.out.println(user);
}
sqlSession.close();
}
8.2、RowBounds分页
1.接口
public interface UserMapper {
List<User> getPageRowBounds();
}
2.mapper.xml
<!--分页-->
<select id="getPageRowBounds" resultMap="UserMapper">
select * from db_test.t_user
</select>
<!--结果集映射-->
<resultMap id="UserMapper" type="User">
<!--column:数据库中的字段名,property实体类中的字段名-->
<result column="id" property="id"/>
<result column="username" property="username"/>
<result column="password" property="pwd"/>
</resultMap>
3.测试
@Test
public void getPageRowBounds() {
//1.获取sqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
//RowBound分页
RowBounds rowBounds = new RowBounds(1,2);
//通过java代码实现分页
List<User> list = sqlSession.selectList("com.liu.dao.UserMapper.getPageRowBounds",null,rowBounds);
for (User user : list) {
System.out.println(user);
}
sqlSession.close();
}
9.注解
大家之前都学过面向对象编程,也学习过接口,但在真正的开发中,很多时候我们会选择面向接口编程.
根本原因: 解耦,可拓展,提高复用,分层开发中,上层不用管具体的实现,大家都遵守共同的标准,使得开发变得容易,规范性更好.
在一个面向对象的系统中,系统的各种功能是由许许多多的不同对象协作完成的。在这种情况下,各个对象内部是如何实现自己的,对系统设计人员来讲就不那么重要了:
而各个对象之间的协作关系则成为系统设计的关键。小到不同类之间的通信,大到各模块之间的交互,在系统设计之初都是要着重考虑的,这也是系统设计的主要工作内容。面向接口编程就是指按照这种思想来编程。
9.1、使用注解开发
原理 类反射
底层 动态代理
1.接口
public interface UserMapper {
@Select("select * from t_user")
List<User> getUsers();
}
2.配置文件绑定接口
<!--绑定接口-->
<mappers>
<mapper class="com.liu.dao.UserMapper"/>
</mappers>
3.测试
@Test
public void getUsers(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> users = mapper.getUsers();
for (User user : users) {
System.out.println(user);
}
sqlSession.close();
}
9.2、基本数据类型加param
public interface UserMapper {
@Select("select * from t_user")
List<User> getUsers();
@Select("select * from t_user where id = #{id}")
User getUsersById(@Param("id") int id);
}
以param的参数为主
10、多对一处理
一对多:集合
多对一:关联
10.1、mapper头部
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--配置-->
<mapper namespace="com.liu.dao.TeacherMapper">
</mapper>
10.2、按照查询嵌套处理
实体类(多对一)
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private int id;
private String name;
private Teacher teacher;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
private int id;
private String name;
}
<!--
1.查询所有的学生
2.根据查询出来的学生的tid,寻找对应的老师
-->
<select id="getstudent" resultMap="StudentTeacher">
select * from student
</select>
<resultMap id="StudentTeacher" type="Student">
<result property="id" column="id"/>
<result property="name" column="name"/>
<!--对象 :association 集合:collection-->
<association property="teacher" column="tid" javaType="Teacher" select="getTacher"/>
</resultMap>
<select id="getTacher" resultType="Teacher">
select * from teacher where id = #{id}
</select>
10.3、按照结果查询处理
<!--按照结果嵌套处理-->
<select id="getstudent2" resultMap="StudentTeacher2">
select s.id sid, s.name sname , t.name tname, from student s, teacher t where s.tid = t.id
</select>
<resultMap id="StudentTeacher2" type="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="Teacher">
<result property="name" column="sname"/>
</association>
</resultMap>
11、一对多处理
实体类(一对多)
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private int id;
private String name;
private int tid;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
private int id;
private String name;
private List<Student> students;
}
按照结果嵌套处理
<!--按照结果嵌套处理-->
<select id="getTacher" resultMap="StudentTeacher">
select s.id sid, s.name sname , t.name tname,t.id tid
from student s, teacher t
where s.tid = t.id and t.id =#{id}
</select>
<resultMap id="StudentTeacher" type="Teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<!-- 集合-->
<collection property="students" ofType="Student">
<result property="name" column="sname"/>
<result property="id" column="sid"/>
<result property="tid" column="tid"/>
</collection>
</resultMap>
12、缓存
1.什么是缓存[ Cache ]?
- 存在内存中的临时数据。
- 将用户经常查询的数据放在缓存(内有中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高井发系统的性能问题。
2.为什么使用缓存?
- 减少和数据库的交互次数,减少系统开销,提高系统效率。
3.什么样的数据能使用缓存?
- 经常查询并且不经常改变的数据。
12.1、mybatis缓存
-
MyBatis包含- 一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。
-
MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存
- 默认情况下,只有一-级缓存开启。(SqISession级别的缓存, 也称为本地缓存)
- 二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
- 为了提高扩展性,MyBatis定义了缓存接口Cache. 我们可以通过实现Cache接口来自定义二级缓存
12.2、一级缓存
- 一级缓存也叫本地缓存:
- 与数据库同一次会话期间查询到的数据会放在本地缓存中。
- 以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库;
默认一级缓存是开启的
增删改操作会刷新缓存(因为有可能会改动数据库)
@Test
public void teacher(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
Teacher tacher = mapper.getTacher(1);
System.out.println(tacher);
sqlSession.clearCache();//关闭一级缓存
sqlSession.close();
}
12.3、二级缓存
-
二级缓存也叫全局缓存,一 级缓存作用域太低了,所以诞生了二级缓存
-
基于namespace级别的缓存,一个名称空间,对应一个二 级缓存;
-
工作机制
- 一个会话查询-条数据,这个数据就会被放在当前会话的一级缓存中;
- 如果当前会话关闭了,这个会话对应的-级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中;
- 新的会话查询信息,就可以从二级缓存中获取内容;
- 不同的mapper查出的数据会放在自己对应的缓存(map) 中;
1.mybatis-config.xml中配置
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
<!--开启全局缓存-->
<setting name="cacheEnabled" value="true"/>
</settings>
2.在mapper中
<mapper namespace="com.liu.dao.TeacherMapper">
<!--在当前mapper.xml中开启二级缓存-->
<cache eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
</mapper>