01Mybatis总结
Mybastis总结
1.什么是mybatis?为什么使用mybatis?
mybatis的前身是ibatis。MyBatis 是一个可以自定义SQL、存储过程和高级映射的持久层框架。MyBatis 摒除了大部分的JDBC代码、手工设置参数和结果集重获。MyBatis 只使用简单的XML 和注解来配置和映射基本数据类型、Map 接口和POJO到数据库记录。相对Hibernate和Apache OJB等“一站式”ORM解决方案而言,Mybatis 是一种“半自动化”的ORM实现。通俗的说,mybatis是用来简化Java后台和数据库之间交互的工具。具体简化内容见下文。文末讲解----使用jdbc开发时,和mybatis相比的不足。---- ----mybatis和hibernate的区别。----
mybatis框架,是一个持久层框架,是apache下的*项目。mybatis让程序员将主要精力放在sql上,通过mytabis提供的映射方式,自动生成满足需要的sql语句mybatis可以向PreparedStatement中输入参数自动进行输入映射,将查询结果集灵活的映射成Java对象(输出映射),输入映射和输出映射这是mybatis的核心 。
==为什么使用mybatis?==MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索封装。MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录。
2.mybatis框架执行流程图
1.执行流程
1.Mybatis 读取config.xml配置文件(mybatis全局配置文件)后会将内容放在一个Configuration类中。
2.SqlSessionFactoryBuilder会读取Configuration类中信息创建SqlSessionFactory。
3.SqlSessionFactory根据需求创建SqlSession(类似connection)(每次访问数据库都需要一个SqlSession)
4.底层(不需要程序员管理)封装对象,向数据库输入参数。
5.使用sqlsession直接或间接(mapper)操作(Update、Select、Insert、Delete)数据库
6.底层使用executor执行器执行操作数据库。
7.底层封装对象,数据库向后台返回所需求的值,也就是针对于数据库来说是输出结果(值或对象)。
2.对于执行流程的详细琐碎解释:
1.Mybatis 读取mybatis-config.xml配置文件(mybatis全局配置文件)后会将内容放在一个Configuration类中,Configuration类会存在整个Mybatis生命周期,以便重复读取。SqlSessionFactoryBuilder会读取Configuration类中信息创建SqlSessionFactory。
2.Mybatis中SqlSessionFactiory、SqlSession等都为接口,Mybatis默认使用的实现类为DefaultSqlSessionFactory和DefaultSqlSession类。
3.SqlSession用途主要有两种
①. 获取对应的Mapper,让映射器通过命名空间和方法名称找到对应的SQL,发送给数据库执行后返回结果。
②. 直接使用SqlSession,通过命名信息去执行SQL返回结果,该方式是IBatis版本留下的,SqlSession通过Update、Select、Insert、Delete等方法操作。
针对Mapper方式,Mybatis底层利用JDK动态代理技术实现该接口,底层最后还是使用的IBatis中SqlSession通过Update、Select、Insert、Delete等方法操作。
4.关于对象的生命周期:
①. SqlSessionFactoryBuilder的作用是创建SqlSessionFactiory,在创建完成后就会被回收,所以它的生命周期只存在于局部方法。
②.SqlSessionFactiory是单例的,作用就是创建SqlSession,每次访问数据库都需要一个SqlSession,所以SqlSessionFactiory的生命周期是贯穿整个Mybatis生命周期的,SqlSessionFactiory采用单例的原因是减少数据库连接资源。
③. SqlSession是一个会话,类似于jdbc的connection,它的生命周期是在请求数据库处理事务过程中,他是一个线程不安全的对象。多线程操作时因多注意他的隔离级别和数据库锁等。SqlSession使用需及时关闭。
④. Mapper是一个接口,它的作用是发送SQL,返回结果,因此他的生命周期不会大于SqlSession。
5.关于SqlSessionFactory的创建,Mybatis采用构造模式来完成创建。
第一步:XMLConfigBuilder解析XML配置,读出配置参数,存入Configuration类中。(底层实现)
第二步:SqlSessionFactoryBuilder会读取Configuration类中的信息创建SqlSessionFactory。
inputStream = Resources.getResourceAsStream(“mybatis-config.xml”);
sqlSessionFactory = new SqlSessionFactoryBuilder.builder(inputStream)inputStream为读取配置文件的流;
6.映射器由MappedStatement、SqlSource、BoundSql三部分组成。
①. MappedStatement,它保存着映射器的一个节点(select|update|insert|delete)。包括许多我们配置的SQL、SQL的ID、缓存信息、resultMap、parameterType、resultType等配置内容。
②. SqlSource,它提供BoundSql对象。是MappedStatement的一个属性。、
③. BoundSql,它是建立SQL和参数的地方,常用的三个属性:SQL、parameterObject、parameterMappings;
parameterObject就是我们传入的参数;
parameterMappings实则是List,ParameterMapping描述了我们的参数,包括属性、名称、表达式、javaType、jdbcType、typeHandler等重要信息,通过它实现参数和SQL的结合,以便PreparedStatement能够通过它找到parameterObject对象的属性并设置参数。
3.代码Mybatis工具类
package com.sz.util;
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 java.io.IOException;
import java.io.InputStream;
public class MybatisUtil {
private static SqlSessionFactory sqlSessionFactory;// 保证单例,由生命周期决定
static{
String resource = "mybatis.cfg.xml";
InputStream in = null;
try {
in = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
} catch (IOException e) {
e.printStackTrace();
}finally {
if (in != null){
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//返回Sqlsession,用其来直接增删查改数据库或者间接通过mapper来操作。
public static SqlSession getSession(){
return sqlSessionFactory.openSession();
}
}
3.具体代码展示,快速入门。简单例子。
1.首先导入关于mybatis的jar包
我这里用的是maven,所以直接在pom.xml中添加依赖即可。因为mybatis是简化后台与数据库交互的,所以我这里添加了MySQL的依赖和log4J日志依赖,方便查看操作。
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis.generator/mybatis-generator-core -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
2.后台与数据库交互,那必然得有数据库连接的信息了,所以得有jdbc.properties
url=jdbc:mysql://localhost:3306/mybatis3
username=root
password=123456
driver=com.mysql.jdbc.Driver
3.我这里用到了log4j日志依赖,方便查操作,所以增加了log4j.properties
# Global logging configuration
log4j.rootLogger=ERROR, stdout
# MyBatis logging configuration...
log4j.logger.com.sz.mapper=DEBUG
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
4.全局配置mybatis.cfg.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="jdbc.properties">
</properties>
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
<setting name="logImpl" value="LOG4J"/>
</settings>
<environments default="dev">
<environment id="dev">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="UNPOOLED">
<property name="url" value="${url}"/>
<property name="driver" value="${driver}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<package name="com.sz.mapper"/>
</mappers>
</configuration>
5.创建一个MybatisUtil.class类,用于获取sqlsession对象
package com.sz.util;
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 java.io.IOException;
import java.io.InputStream;
public class MybatisUtil {
private static SqlSessionFactory sqlSessionFactory;//保证了单例
static{
String resource = "mybatis.cfg.xml";
InputStream in = null;
try {
in = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
} catch (IOException e) {
e.printStackTrace();
}finally {
if (in != null){
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static SqlSession getSession(){
return sqlSessionFactory.openSession();
}
}
6.创建一个简单的实例Girl.class
package com.sz.pojo;
import java.util.Date;
public class Girl {
private Long id;
private String lastName;
private String flower;
private Date birthday;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getFlower() {
return flower;
}
public void setFlower(String flower) {
this.flower = flower;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
7.创建对应的GirlMapper.class
package com.sz.mapper;
import com.sz.pojo.Girl;
import org.apache.ibatis.annotations.Param;
import java.util.Map;
public interface GirlMapper {
int insert(Girl girl);
Girl queryById(Long id);
Girl queryById2(long id);
Girl queryByName(String name);
Girl queryByNameFlower(@Param("name") String name, @Param("flower") String flower);
Girl queryByNameFlower2(Girl girl);
Girl queryByNameFlower3(Map<String,Object> map);
}
8.创建与GirlMapper.class对应的GirlMapper.xml文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//ibatis.apache.org//DTD Mapper 3.0//EN" "http://ibatis.apache.org/dtd/ibatis-3-mapper.dtd">
<mapper namespace="com.sz.mapper.GirlMapper">
<insert id = "insert">
insert into girl (name,flower,birthday) values (#{name},#{flower},#{birthday})
</insert>
<select id="queryById" resultType="com.sz.pojo.Girl">
select * from girl where id = #{id}
</select>
<select id="queryById2" resultType="com.sz.pojo.Girl">
select * from girl where id = #{id}
</select>
<select id ="queryByName" resultType="com.sz.pojo.Girl">
select * from girl where last_name = #{name}
</select>
<!--使用默认的参数名称风格,非常不友好,这种方式是断然不可取的。-->
<select id ="queryByNameFlower" resultType="com.sz.pojo.Girl">
<!--select * from girl where last_name = #{param1} and flower = #{param2}-->
<!--select * from girl where last_name = #{arg0} and flower = #{arg1}-->
select * from girl where last_name = #{name} and flower = #{flower}
</select>
<select id ="queryByNameFlower2" resultType="com.sz.pojo.Girl">
select * from girl where last_name = #{lastName} and flower = #{flower}
</select>
<select id ="queryByNameFlower3" resultType="com.sz.pojo.Girl">
select * from girl where last_name = #{lastName} and flower = #{flower}
</select>
<select id="queryByAB" resultType="com.sz.pojo.Girl">
<!--select * from girl where last_name = #{arg0.name} and flower = #{arg1.flower}-->
select * from girl where last_name = #{a.name} and flower = #{b.flower}
</select>
</mapper>
9.测试类
@Test
public void m2(){
SqlSession sqlSession = MybatisUtil.getSession();
GirlMapper mapper = sqlSession.getMapper(GirlMapper.class);
Girl girl = mapper.queryById(1L);
System.out.println(girl);
System.out.println(girl.getLastName());
sqlSession.close();
}
4.传统JDBC操作数据库
1.创建连接,2.操作数据
package com.jdbc.example;
import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class JdbcExample {
//创建一个数据库的连接
private static Connection getConnection() {
Connection connection = null;
try {
Class.forName("com.mysql.jdbc.Driver");//加载用户驱动
String url = "jdbc:mysql://localhost:3306/mybatis";//连接数据库的地址
String user = "root";//数据库的用户名
String password = "root";//数据库的密码
connection = DriverManager.getConnection(url, user, password);//得到一个数据库的连接
} catch (ClassNotFoundException e) {
// TODO 自动生成的 catch 块
System.out.println(JdbcExample.class.getName() + "数据库驱动包未找到!");
return null;
} catch (SQLException e) {
// TODO 自动生成的 catch 块
System.out.println(JdbcExample.class.getName() + "SQL语句有问题,无法查询成功!");
return null;
}
return connection;//返回该连接
}
public User getUser(int id) {
Connection connection = getConnection();//得到该数据库的连接
PreparedStatement ps = null;//声明一个null的预处理的Statement
ResultSet rs = null;//声明一个结果集,用来存放SQL的查询后的结果
try {
ps = connection.prepareStatement("select * from user where id=?");//对查询的User表的SQL进行预处理编译
ps.setInt(1, id);//把参数Id设值到数据的条件中
rs = ps.executeQuery();//执行查询语句。把结果返回到ResultSet结果集中
while (rs.next()) {//遍历从结果集中取数
int user_id = rs.getInt("id");//取出Statement的用户id
String username = rs.getString("username");//取出Statement的用户名
Date birthday = rs.getDate("birthday");//取出Statement的生日
String sex = rs.getString("sex");//取出Statement的性别
String address = rs.getString("address");//取出Statement的用户地址
User user = new User();//创建一个User类的实体对象POJO
user.setId(user_id);//存放在user对象中
user.setUsername(username);
user.setBirthday(birthday);
user.setSex(sex);
user.setAddress(address);
return user;
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
this.close(rs, ps, connection);
}
return null;
}
//判断数据库是否关闭
/***
*
* @param rs 查看结果集是滞关闭
* @param stmt 预处理SQL是否关闭
* @param conn 数据库连接是否关闭
*/
private void close(ResultSet rs, Statement stmt, Connection conn) {
try {
if (rs != null) {
rs.close();
}
} catch (SQLException e) {
System.out.println(JdbcExample.class.getName() + "ResultSet 关闭失败!");
}
try {
if (stmt != null) {
stmt.close();
}
} catch (SQLException e) {
System.out.println(JdbcExample.class.getName() + "Statement 关闭失败!");
}
try {
if (conn != null) {
conn.close();
}
} catch (SQLException e) {
System.out.println(JdbcExample.class.getName() + "Connection 关闭失败!");
}
}
public static void main(String[] args) {
User user = new JdbcExample().getUser(1);//我们查询用户的id 为 1 用户
System.out.println(user);//打印输出查询出来的数据
}
}
5.JDBC与mybatis比较
1,数据库连接,使用时就创建,不使用就释放,对数据库进行频繁连接开关和关闭,造成数据库资源浪费,影响数据库的性能
解决:使用数据库连接池管理数据库的连接
2,sql语句使用硬编码在java程序中,修改sql语句,就需要重新编译java代码,不利于系统维护
解决:把sql语句放在xml配置文件中,修改sql语句也不需要重新编译java代码
3,向预编译语句PreparedStatement中设置参数,对占位符位置和设置参数值,硬编码,修改sql语句也不需要重新编译java代码
解决:把sql语句和占位符设置参数值放在xml配置文件中
4,从result中遍历结果集数据时,存在硬编码,将获取表的字段进行硬编码。
解决:将查询的结果集,自动映射成 java对象。
6.Mybatis与Hibernate比较
https://blog.csdn.net/fupengyao/article/details/51538004
7.详解标签
详细见官方文档http://www.mybatis.org/mybatis-3/zh/configuration.html。核心是下面的1,2,3.
1.全局xml配置标签
MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。 配置文档的顶层结构如下:
configuration(配置)
properties(属性) ********************常用*************************
settings(设置) ********************常用*************************
typeAliases(类型别名) ********************常用*************************
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置) ********************常用*************************
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器) ********************常用*************************
常用的必须通读官方文档。
补充:
1.dataSource里面的字面值属性优先级>>>外部的properties文件里面的属性的优先级>>>properties里面的子节点的属性
2.一个配置完整的 settings 元素的示例如下:
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="multipleResultSetsEnabled" value="true"/>
<setting name="useColumnLabel" value="true"/>
<setting name="useGeneratedKeys" value="false"/>
<setting name="autoMappingBehavior" value="PARTIAL"/>
<setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
<setting name="defaultExecutorType" value="SIMPLE"/>
<setting name="defaultStatementTimeout" value="25"/>
<setting name="defaultFetchSize" value="100"/>
<setting name="safeRowBoundsEnabled" value="false"/>
<setting name="mapUnderscoreToCamelCase" value="false"/>
<setting name="localCacheScope" value="SESSION"/>
<setting name="jdbcTypeForNull" value="OTHER"/>
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>
3.mapper标签:这些配置会告诉了 MyBatis 去哪里找映射文件也就是对应每个class的xml文件(写具体sql语句的文件),剩下的细节就应该是每个 SQL映射文件了,也就是接下来我们要讨论的。
2.xml映射文件
3.动态SQL
4.Java API
5.日志
8.概念定义补充
1.域对象
域对象是所有应用程式的中枢。他们捕捉了数据库的核心数据模型和应用在数据上的业务规则。在典型情况下,应用程式的大多数子系统都依赖这些通用的域对象–这意味着域对象的映射越接近数据库大纲,应用程式研发者理解和使用他们就越容易,因为他们表现了数据库中的现实"实体"和"关系"。
在Java中可以说是可以拥有增删改查功能的一个Javabean类。Java中的三个域对象:servletContext—request—session。在数据库中域对象可以理解为就是一张表里面的一条数据信息。
2.持久化
什么是持久化
持久化是将程序数据在持久状态和瞬时状态间转换的机制。通俗的讲,就是瞬时数据(比如内存中的数据,是不能永久保存的)持久化为持久数据(比如持久化至数据库中,能够长久保存)。
狭义的理解: “持久化”仅仅指把域对象永久保存到数据库中;广义的理解,“持久化”包括和数据库相关的各种操作。
● 保存:把域对象永久保存到数据库。
● 更新:更新数据库中域对象的状态。
● 删除:从数据库中删除一个域对象。
● 加载:根据特定的OID,把一个域对象从数据库加载到内存。
● 查询:根据特定的查询条件,把符合查询条件的一个或多个域对象从数据库加载内在存中。
为什么要持久化?
持久化技术封装了数据访问细节,为大部分业务逻辑提供面向对象的API。
● 通过持久化技术可以减少访问数据库数据次数,增加应用程序执行速度;
● 代码重用性高,能够完成大部分数据库操作;
● 松散耦合,使持久化不依赖于底层数据库和上层业务逻辑实现,更换数据库时只需修改配置文件而不用修改代码。
3.ORM
对象关系映射(英语:(Object Relational Mapping,简称ORM,或O/RM,或O/R mapping),是一种程序技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换 。从效果上说,它其实是创建了一个可在编程语言里使用的–“虚拟对象数据库”。
面向对象是从软件工程基本原则(如耦合、聚合、封装)的基础上发展起来的,而关系数据库则是从数学理论发展而来的,两套理论存在显著的区别。为了解决这个不匹配的现象,对象关系映射技术应运而生。
简单的说:ORM相当于中继数据。
4.映射
通俗的说,就是同一种蔬菜土豆的不同表现,生土豆到熟土豆。在Java后台和数据库交互之间的映射就是说,将JavaBean类的属性翻译为数据库中的属性。本质上是一样的,只是类型的转变。方便交互统一交流。也可以比喻为英语和汉语,hello和你好,本质上意思一样,但是只会说汉语的可能不明白hello,所以要将hello转为汉语,才能明白。这就是类型的转变。本质没有改变,只是为了更好的沟通。在技术层面上可能涉及到由复杂到简单的表现存储等。
5.依赖
举个例子,我饿了,我要吃食物。那么现在食物就是能够让我生存的依赖。我依赖于食物生存。食物就是依赖。我依赖于食物。对于一个Java项目而言,往往需要很多不同的jar包来实现业务逻辑功能,而对于项目而言,jar包就是这个项目的依赖。添加依赖即为添加jar包。
6.OGNL
OGNL(对象图导航语言)是Object-Graph Navigation Language的缩写,它是一种功能强大的表达式语言,通过它简单一致的表达式语法,可以存取对象的任意属性,调用对象的方法,遍历整个对象的结构图,实现字段类型转化等功能。它使用相同的表达式去存取对象的属性。
9.缓存
一级缓存是存在一个sqlsession中的
二级缓存是存在多个sqslsession中的,跨级。范围更广
https://blog.csdn.net/llziseweiqiu/article/details/79413130 参考
Mybatis的一级缓存和二级缓存执行顺序
1、先判断二级缓存是否开启,如果没开启,再判断一级缓存是否开启,如果没开启,直接查数据库
2、如果一级缓存关闭,即使二级缓存开启也没有数据,因为二级缓存的数据从一级缓存获取
3、一般不会关闭一级缓存
4、二级缓存默认不开启
5、如果二级缓存关闭,直接判断一级缓存是否有数据,如果没有就查数据库
6、如果二级缓存开启,先判断二级缓存有没有数据,如果有就直接返回;如果没有,就查询一级缓存,如果有就返回,没有就查询数据库;
https://blog.csdn.net/jinhaijing/article/details/84257981 参考