简介
1、什么是 MyBatis?
MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生类型、接口和 Java 的 POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
2、Mybatis架构
根据上图,我们可以看出,实现mybatis访问后台db到展示有以下几个步骤:
1、首先为mybatis配置SqlMapConfig.xml,此文件作为mybatis的全局配置文件,配置了mybatis的运行环境等信息。mapper.xml文件即sql映射文件,文件中配置了操作数据库的sql语句。此文件需要在SqlMapConfig.xml中加载。
2、通过mybatis环境等配置信息构造SqlSessionFactory(会话工厂 )
3、由SqlSessionFactory(会话工厂)创建sqlSession即会话,操作数据库需要通过sqlSession进行。
4、mybatis底层自定义了Executor执行器接口操作数据库,Executor接口有两个实现,一个是基本执行器、一个是缓存执行器。
5、Mapped Statement也是mybatis一个底层封装对象,它包装了mybatis配置信息及sql映射信息等。mapper.xml文件中一个sql对应一个Mapped Statement对象,sql的id即是Mapped statement的id。
6、Mapped Statement对sql执行输入参数进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql前将输入的java对象映射至sql中,输入参数映射就是jdbc编程中对preparedStatement设置参数,这边是使用的预编译功能,预编译在一定程度上可以防止sql注入问题。
7、Mapped Statement对sql执行输出结果进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql后将输出结果映射至java对象中,输出结果映射过程相当于jdbc编程中对结果的解析处理过程。
补充说明:预编译是什么?有什么优点?
预编译:简言之,预编译又称为预处理,是做些代码文本的替换工作。是整个编译过程的最先做的工作。
sql注入:
strSQL =
"SELECT * FROM users WHERE name = '"
+ userName +
"' and pw = '"
+ passWord +
"';"
假如我们填入:
userName =
"1' OR '1'='1"
;
passWord =
"1' OR '1'='1"
;
那么整条sql将变为:
SELECT * FROM users WHERE name = '1' OR '1'='1' and pw = '1' OR '1'='1';
因为where条件恒为真,所以该sql等同于SELECT * FROM users,如果用户在后面在加上; DROP TABLE users,那么未登陆情况下就可以删除表,该影响将不可预估。
预编译情况:
String sql="select * from users"+
"where name = ? ";
try {
PreparedStatement pset_f = conn.prepareStatement(sql);
pset_f.setString(1,userName[j]);
pset_f.executeUpdate(sql_update);
}catch(Exception e){
//e.printStackTrace();
logger.error(e.message());
}
从例子中可以看出,预编译使用?作为占位符,通过PreparedStatement对象进行设值,而且一个占位符只能允许一个值,在程序运行时第一次操作数据库之前,SQL语句已经被数据库分析,编译和优化,对应的执行计划也会缓存下来并允许数据库已参数化的形式进行查询,当运行时动态地把参数传给PreprareStatement时,即使参数里有敏感字符如 or '1=1'也数据库会作为一个参数一个字段的属性值来处理而不会作为一个SQL指令,如此,就起到了SQL注入的作用了!
在使用参数化查询的情况下,数据库系统(eg:MySQL)不会将参数的内容视为SQL指令的一部分来处理,而是在数据库完成SQL指令的编译后,才套用参数运行,因此就算参数中含有破坏性的指令,也不会被数据库所运行。
预编译是如何运作的:
SELECT * FROM users WHERE name = '"
+ userName +
"';
传值:
userName =
" 1' OR 1=1 "
最终变为:
SELECT * FROM users WHERE name =
'1'
' OR 1=1
这样数据库就会去系统查找name为“1′ ‘ OR 1=1”的记录,而避免了SQL注入。
优点:
1.PreparedStatement能预编译,这条预编译的SQL查询语句能在将来的查询中重用,这样一来,它比Statement对象生成的查询速度更快。
2.PreparedStatement可以写动态参数化的查询
3.PreparedStatement可以防止SQL注入式攻击
4.PreparedStatement查询可读性更好,追加条件的语句很乱
5.PreparedStatement不允许一个占位符(?)有多个值
3、小试牛刀
a、创建一个java项目
b、引入maven依赖
c、创建数据库和表
执行脚本:
create database mybatis_test; use mybatis_test; CREATE TABLE users(id INT PRIMARY KEY AUTO_INCREMENT, NAME VARCHAR(20), age INT); INSERT INTO users(NAME, age) VALUES('张三', 13); INSERT INTO users(NAME, age) VALUES('李四', 25);
d、添加配置:mybatis-config.xml
在src目录下的resource下创建一个mybatis-config.xml文件,如下图所示:
1) mybatis-config.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>
<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/mybatis_test" />
<property name="username" value="root" />
<property name="password" value="root" />
</dataSource>
</environment>
</environments>
</configuration>
2) 使用mybatis-generator 生成工具生成 实体、mapper文件和 Dao接口如下:
实体:User.java
@Data
public class User {
//实体类的属性和表的字段名称一一对应
private int id;
private String name;
private int age;
}
mapper文件: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">
<!-- 为这个mapper指定一个唯一的namespace,namespace的值习惯上设置成包名+sql映射文件名,这样就能够保证namespace的值是唯一的
例如namespace="com.tcyl.wyf.userMapper"就是com.tcyl.wyf包名)+userMapper(userMapper.xml文件去除后缀)
-->
<mapper namespace="com.tcyl.wyf.mapping.userMapper">
<!-- 在select标签中编写查询的SQL语句, 设置select标签的id属性为getUser,id属性值必须是唯一的,不能够重复
使用parameterType属性指明查询时使用的参数类型,resultType属性指明查询返回的结果集类型
resultType="me.gacl.domain.User"就表示将查询结果封装成一个User类的对象返回
User类就是users表所对应的实体类
-->
<!--
根据id查询得到一个user对象
-->
<select id="getUser" parameterType="int"
resultType="com.tcyl.wyf.entity.User">
select * from users where id=#{id}
</select>
</mapper>
3) 在mybatis-config中添加如下配置:
<mappers>
<!-- 注册userMapper.xml文件,
userMapper.xml位于com.tcyl.wyf.mapping这个包下,所以resource写成com/tcyl/wyf/mapping/userMapper.xml-->
<mapper resource="com/tcyl/wyf/mapping/userMapper.xml"/>
<!-- 也可以通过方式
<package name="com.tcly.wyf.mapping"/>-->
</mappers>
4) 编写测试类
package com.tcly.wyf.test;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import com.tcly.wyf.entity.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;
public class Test1 {
public static void main(String[] args) throws IOException {
//mybatis的配置文件
String resource = "resource/mybatis-config.xml";
//使用类加载器加载mybatis的配置文件(它也加载关联的映射文件)
InputStream is = Test1.class.getClassLoader().getResourceAsStream(resource);
//构建sqlSession的工厂
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);
//使用MyBatis提供的Resources类加载mybatis的配置文件(它也加载关联的映射文件)
//Reader reader = Resources.getResourceAsReader(resource);
//构建sqlSession的工厂
//SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
//创建能执行映射文件中sql的sqlSession
SqlSession session = sessionFactory.openSession();
/**
* 映射sql的标识字符串,
* me.gacl.mapping.userMapper是userMapper.xml文件中mapper标签的namespace属性的值,
* getUser是select标签的id属性值,通过select标签的id属性值就可以找到要执行的SQL
*/
String statement = "me.gacl.mapping.userMapper.getUser";//映射sql的标识字符串
//执行查询返回一个唯一user对象的sql
User user = session.selectOne(statement, 1);
System.out.println(user);
}
}
3、和Hibernate对比
hibernate 执行流程
myBatis 执行流程
异同:
相同点
1.都是对象关系映射(Object Relational Mapping)框架,体现为都提供实体类和数据库表相互映射的功能。
2.业务逻辑代码层面的操作基本一致。
Hibernate与MyBatis都可以是通过SessionFactoryBuider由XML配置文件生成SessionFactory,然后由SessionFactory 生成Session,最后由Session来开启执行事务和SQL语句。其中SessionFactoryBuider,SessionFactory,Session的生命周期都是差不多的。
3、Hibernate和MyBatis都支持JDBC和JTA事务处理。
区别
1.hibernate设计理念是完全面向POJO(Plain Ordinary Java Object)的,所以使用者可以基本不用书写sql就能通过配置的映射关系完成数据库操作,但是mybatis不一样,他需要我们手动书写sql。总的来说,hibernate的优势在于能让程序开发人员更多的关注业务实现,而不是sql书写(框架会根据映射关系自动生成sql)。但是,mybatis虽然需要我们自己书写sql和接口,开发工作量较大,但是它比起hibernate更加灵活,并且能够不断的优化sql(因为是自定义的),也就是具有更强的灵活性和可优化性。
2.hibernate通常用于传统管理系统的开发,而mybatis广泛的应用于互联网开发(因为互联网需要灵活和可优化,毕竟一条sql的执行时间在优化前和优化后差别很大)
3、从开发速度来比较,Mybatis其实要比Hibernate要更好上手,因为Hibernate是对JDBC的深度封装,而Mybatis就显得更加开放,而且简单易学。这也是Mybatis更加流行的原因,正因为如此,Mybatis的开发社区近年来也开始活跃起来,下载一些支持开发的工具也较为方便;Mybatis也有自己的代码生成工具,可以生成简单基本的DAO层方法,针对高级查询,Mybatis需要手动编写SQL语句,以及ResultMap。而Hibernate有良好的映射机制。Hibernate数据库移植性很好,MyBatis的数据库移植性不好,不同的数据库需要写不同SQL。
总结:
myBatis 的定位
myBatis 专注于sql 本身,其为sql 映谢而非完整的ORM,需要自己编写sql 语句,这是其优点也是缺点。优点是:优化方便,可更好利用sql编写经验。缺点是当数据修改之后调整麻烦耗费时间长.
试用场景:适用于对性能要求较高,有大批量的查询修改,并且业务实现没有过多依懒数据关系模型,比如:电商、O2O等互联网项目。
Tips:
1、‘#’和‘$’的区别
sql 预编译指的是数据库驱动在发送 sql 语句和参数给 DBMS 之前对 sql 语句进行编译,这样 DBMS 执行 sql 时,就不需要重新编译。
‘#{ }’:解析为一个 JDBC 预编译语句(prepared statement)的参数标记符,一个‘ #{ }’ 被解析为一个参数占位符 ? 。
‘${ }’ 仅仅为一个纯粹的 string 替换,在动态 SQL 解析阶段将会进行变量替换。在预编译之前已经被变量替换了
‘${ }’变量的替换阶段是在动态 SQL 解析阶段,而’#{ }’变量的替换是在 DBMS 中。
2、Collection和Association的区别
关联-association 集合-collection
association是用于一对一和多对一,而collection是用于一对多的关系