MyBatis是java平台下一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object,简单普通的 Java对象)映射成数据库中的记录。
特点
1.轻量级⾃身不依赖其他任何JAR,但需要提供JDBC实现
2.灵活,更加适⽤于需求变化频繁的互联⽹应⽤
3.学习成本低,相⽐ORM框架⽽⾔,掌握MyBatis的使⽤是很轻松的
ORM:对象关系型映射,完全封装了sql语句,不用自己写SQL语句
mybatis在MVC中的位置
mybatis处于结构中的Dao层,作用是:
连接数据库
接收输入数据
拼接并执行SQL
解析并返回结果
为什么需要MyBatis
JDBC存在以下问题:
1、接受参数拼接SQL语句并执行
JDBC每一条SQL语句都是直接写在代码中(硬编码),如果后期需求发生变化,则需要修改源码中的SQL,然后重新编译,测试
MyBatis将数据库连接相关的参数放到配置XML中并封装了创建连接的代码
2、解析结果
JDBC返回的是ResultSet,必须手动将其映射到一个个的对象中,同样是重复度很高的代码;并且存在硬编码问题
MyBatis实现了入参映射到SQL参数,以及结果集映射到POJO对象
3、每次操作都需要手动的创建连接,最后关闭连接
JDBC对于重复代码通常开发者都会进行封装,但是由于每个人的编码风格不同导致封装的代码也没有固定的套路
MyBatis将数据库连接相关的参数放到配置XML中并封装了创建连接的代码
4、频繁的创建和销毁连接
由于数据库连接使用的是TCP长连接,并发量大的系统中,这样的方式会导致数据库连接资源耗尽
MyBatis本身实现了连接池,可以解决这一问题,当然后续会更换其他更好的连接池
更多功能
MyBatis在解决上述问题的同时提供了更多实用的功能:
动态SQL,即在SQL语句中可以包含逻辑处理(判断,循环等....)
高级映射,支持一对一,一对多映射
动态代理Mapper,使得可以用面向对象的方式来完成数据库操作
逆向工程,根据表结构自动生成,POJO,Mapper映射和Mapper接口,包含简单的增删改查
MyBatis构架
MybatisConfig.xml作为全局配置,指定MyBatis的基本参数,如运行环境(开发,发布),事务管理器,数据来源等; 以及需要加载的mapper映射文件(从源码中剥离出来的SQL语句)
SqlSessionFactory,负责读取SqlMapConfig中的参数创建会话
SqlSession,通过SqlSessionFactory获取一个Session(会话)
Executor 真正负责执行sql语句的对象
MappedStatement用于将输入参数映射到sql语句,以及结果集映射到POJO
上述构架中,SqlSession以下的部分是MyBatis封装好的,SqlSession负责调用它们完成操作; 开发过程中不需要涉及(特殊需求除外);
另外SqlSessionFactory和SqlSession也可以通过简单的代码获取到,后续Spring框架能够自动创建它们
所以使用MyBatis的重点就落在了MybatisConfig.xml以及Mapper.xml中
MyBatis的功能架构
我们把Mybatis的功能架构分为三层:
1、API接口层:提供给外部使用的接口API,开发人员通过这些本地API来操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。
2、数据处理层:负责具体的SQL查找、SQL解析、SQL执行和执行结果映射处理等。它主要的目的是根据调用的请求完成一次数据库操作。
3、基础支撑层:负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是共用的东西,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的支撑。
MyBatis的优缺点
优点:
1、简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置1几个sql映射文件易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。
2、灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。 sql写在xml里,便于统一管理和优化。通过sql基本上可以实现我们不使用数据访问框架可以实现的所有功能,或许更多。
3、解除sql与程序代码的耦合:通过提供DAL层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。
4、提供映射标签,支持对象与数据库的orm字段关系映射
5、提供对象关系映射标签,支持对象关系组建维护
6、提供xml标签,支持编写动态sql。
缺点:
编写SQL语句时工作量很大,尤其是字段多、关联表多时,更是如此。
SQL语句依赖于数据库,导致数据库移植性差,不能更换数据库。
框架还是比较简陋,功能尚有缺失,虽然简化了数据绑定代码,但是整个底层数据库查询实际还是要自己写的,工作量也比较大,而且不太容易适应快速数据库修改。
二级缓存机制不佳
mybatis入门
官方文档:https://mybatis.org/mybatis-3/getting-started.html
1、创建项目
这里采用Maven来引入MyBatis,创建一个maven项目
2、添加依赖
在pom.xml依赖中添加下面依赖:
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis mybatis依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/log4j/log4j 日志依赖-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java mysql连接驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!-- 测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
3、提供配置文件
MyBatis全局配置
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"/>
<!-- 指定数据源 就是数据来⾃哪⾥ 这⾥默认使⽤MyBatis⾃带的连接池-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<!-- //表示本机localhost &==& xml中&需要转译-->
<property name="url" value="jdbc:mysql:///mybatisDB?characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!-- 指定要加载的映射⽂件-->
<mappers>
<mapper resource="mapper/ProductsMapper.xml"/>
</mappers>
</configuration>
数据库创建
CREATE DATABASE IF NOT EXISTS `mybatisDB` CHARACTER SET utf8;
USE `mybatisDB`;
DROP TABLE IF EXISTS `products`;
CREATE TABLE `products` (
`pid` int(11) NOT NULL AUTO_INCREMENT,
`pname` varchar(20) DEFAULT NULL,
`price` double DEFAULT NULL,
`pdate` date DEFAULT NULL,
`cid` varchar(20) DEFAULT NULL,
PRIMARY KEY (`pid`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
INSERT INTO `products` VALUES (2,'新疆⼤枣',38,NULL,'s002'),(3,'新疆切
糕',68,NULL,'s001'),(4,'⼗三⾹',10,NULL,'s002'),(5,'⽼⼲爹',20,NULL,'s002'),(6,'泰
国咖喱',50.5,'2020-01-02','s001'),(7,'泰国咖喱2',50.5,'2020-01-02','s001');
log4日志模块,定义输出格式的配置
log4j.properties
# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# 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
通过配置mapper.xml,直接操作数据库:
ProductsMapper.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">
<!--namespace ⽤于多个Mapper出现相同的sql时 区分不同包-->
<mapper namespace="mapper.ProductsMapper">
<!-- 通过id查询 #{}代表取值-->
<select id="selectById" parameterType="int" resultType="po.Product">
select * from products where pid = #{pid}
</select>
<!-- 模糊查询 在字符串里取值就不能用#{} 而要用${}-->
<select id="selectById" parameterType="String" resultType="po.Product">
select * from products where pname like "%${pid}%"
</select>
<!-- 插入-->
<insert id="insertProduct" parameterType="po.Product" keyProperty="pid" useGeneratedKeys="true">
insert into products values (null,#{pname},#{price},#{pdate},#{cid})
</insert>
<!--更新-->
<update id="updateProduct" parameterType="po.Product">
update products set price = #{price} where pid = #{pid}
</update>
<!--删除-->
<delete id="deleteProduct" parameterType="int">
delete from products where pid = #{pid}
</delete>
</mapper>
注:
#{} 表示⼀个站位符等同于 ?,可以用来取值,若输⼊参数是基础数据类型则可以随意写,若输⼊是⼀个POJO则写属性名称,但是如何是在字符串中就不能用了,而要用${} 如:pid = #{pid} ,pid like “%${pid}%”
parameterType 输入映射 表示 sql语句接受的参数类型,参数在sql语句中直接通过 #{} 或${} 取。
resultType 输出映射,表示将结果映射到设定的类型中
设置好了要在mybatis-config.xml 中配置:
<!-- 指定要加载的映射⽂件-->
<mappers>
<mapper resource="mapper/ProductsMapper.xml"/>
</mappers>
至此上面就可以用了,下面是查询测试代码:
@Test
public void secletByIdTest() throws IOException {
//创建工厂创建类
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//加载配置文件
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
//创建会话工程
factory = sqlSessionFactoryBuilder.build(in);
try (SqlSession session = factory.openSession()) {
//不使用动态代理 selectById 对应配置文件设置的id
Product product = session.selectOne("selectById", 3);
Product product = mapper.selectById(3);
System.out.println(product);
}
}
上面有增删改查,具体测试步骤是一样的,调用步骤都是:
1、创建工厂创建类
2、加载配置文件
3、创建会话工程
4.获取会话
5.执行方法
上面步骤是可以执行的,但是在开发时,虽然把sql语句集中起来,但是在开发时也出现了问题,那就是上层在调用时,我不知道该传什么参数,要去查配置文件,开发起来十分麻烦。所以mybatis提供了一个动态代理;
动态代理
在开发时我们一般都是面向接口编程,在mybatis里也设置了这一点,所以它采用了动态代理,就是设置一个接口,然后把接口和配置文件关联起来,mybatis根据配置文件,动态代理生成实现类:
上面ProductsMapper.xml文件 对应的接口 ProductsMapper.java:
public interface ProductsMapper {
//按id查询
Product selectById(int pid);
//模糊查询 按名字
List<Product> selectByName(String pname);
//插入并返回pid
void insertProduct(Product product);
//修改
void updateProduct(Product product);
//删除
void deleteProduct(int pid);
}
注意:
两个文件名字可以不一样,但是编码规范,一般设置成一样的。
xml配置文件中mapper标签 namespace="mapper.ProductsMapper" 此为对应接口的包名加类名
xml配置文件中增删改查标签中 id="updateProduct" 此为对应接口声明的的对应方法名
增删改查标签中 parameterType="po.Product" 输入映射与参数相同,输出映射与返回值相同,没有就不写
下面是对应的测试方法:
public class ProductsMapperTest {
private SqlSessionFactory factory;
@Before
public void init() throws IOException {
//创建工厂创建类
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//加载配置文件
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
//创建会话工程
factory = sqlSessionFactoryBuilder.build(in);
}
@Test
public void secletByIdTest(){
try (SqlSession session = factory.openSession()) {
//不使用动态代理
//Product product = session.selectOne("selectById", 3);
//使用动态代理
ProductsMapper mapper = session.getMapper(ProductsMapper.class);
Product product = mapper.selectById(3);
System.out.println(product);
}
}
@Test
public void selectByNameTest(){
try (SqlSession session = factory.openSession()) {
ProductsMapper mapper = session.getMapper(ProductsMapper.class);
List<Product> list = mapper.selectByName("新疆");
System.out.println(list);
}
}
@Test
public void insertProductTest(){
try (SqlSession session = factory.openSession(true)) {
ProductsMapper mapper = session.getMapper(ProductsMapper.class);
Product product = new Product("新疆哈密瓜",50.5,new Date(),"s001");
mapper.insertProduct(product);
System.out.println(product);
}
}
@Test
public void updateProductTest(){
try (SqlSession session = factory.openSession(true)) {
ProductsMapper mapper = session.getMapper(ProductsMapper.class);
Product product = new Product();
product.setPrice(20);
product.setPid(9);
mapper.updateProduct(product);
}
}
@Test
public void deleteProduct(){
try (SqlSession session = factory.openSession(true)) {
ProductsMapper mapper = session.getMapper(ProductsMapper.class);
mapper.deleteProduct(9);
}
}
}
上面测试方法中,前几步都一样,差别在获取session之后,是代理生成实现类:
session.getMapper(ProductsMapper.class);
通过实现类再进行执行增删改下方法。
注:一般开发都是先写好接口,再编写配置文件。
链接:mybatis进阶