MyBatis入门
一、ORM和JDBC对比
JDBC
ORM
编程复杂
相对简单
需要手动关闭连接
引入连接池,不需要手动关闭
业务与技术代码耦合
代码解耦
二、MyBatis和Hibernate对比
MyBatis
Hibernate
半自动ORM框架
全自动ORM框架
高度灵活,必须写SQL
全表映射不方便,无法自定义组装SQL,不要写SQL
基于SQL优化方便
HQL黑盒封装,调优复杂
开发人员自定义SQL
复杂SQL支持若
性能好
性能相对低
三、MyBatis三要素
POJO:Java实体类
映射规则:每一个Java接口对应一个xml文件,接口文件中方法对应xml中的一个节点,节点内包含sql语句和映射规则
SQL语句:对接数据库
四、MyBatis核心类引入
SqlSessionFactoryBuilder:通过建造者模式,辅助构建SqlSessionFactory对象
SqlSessionFactory:创建SqlSession的工厂,单例模式,存在与整个程序的生命周期
SqlSession:代表一次数据库链接,线程不安全,需要线程独享(方法级),具备执行sql的能力
SQL Mapper:由java接口文件和Xml文件组成,包含需要执行上午sql语句和结果集映射,方法级别生命周期,一次SqlSession可能会访问多次数据库。
五、入门案例
5.1 Maven依赖
< dependency>
< groupId> mysql< / groupId>
< artifactId> mysql- connector- java< / artifactId>
< version> 5.1 .19 < / version>
< / dependency>
< ! -- mybatis相关依赖 -- >
< dependency>
< groupId> org. mybatis< / groupId>
< artifactId> mybatis< / artifactId>
< version> 3.4 .1 - moping< / version>
< / dependency>
5.2 创建属性文件
使用属性文件db.properties保存数据库配置,然后再MyBatis的主配置文件引入该文件,保存在resources目录下面
jdbc_driver=com.mysql.jdbc.Driver
jdbc_url=jdbc:mysql://192.168.11.27:3306/demo?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true
jdbc_username=root
jdbc_password=introcks1234
5.3 创建MyBatis配置文件
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>
<!--引入属性文件-->
<properties resource="db.properties"/>
<!--配置environment环境 -->
<environments default="development">
<!-- 环境配置1,每个SqlSessionFactory对应一个环境 -->
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc_driver}"/>
<property name="url" value="${jdbc_url}"/>
<property name="username" value="${jdbc_username}"/>
<property name="password" value="${jdbc_password}"/>
</dataSource>
</environment>
</environments>
<!-- 映射文件,mapper的配置文件 -->
<mappers>
<!--直接映射到相应的mapper文件 -->
<mapper resource="mybatis/mapper/ItemMapper.xml"/>
</mappers>
</configuration>
5.4 创建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 = " item" >
< insert id = " addItem" parameterType = " com.intellif.mozping.entity.Item" >
insert into tb_item(id,description)values(#{id},#{description})
</ insert>
< delete id = " deleteItemById" parameterType = " java.lang.Integer" >
DELETE FROM tb_item where id = #{id}
</ delete>
< update id = " updateItemById" parameterType = " com.intellif.mozping.entity.Item" >
UPDATE tb_item set description=#{description} where id = #{id}
</ update>
< select id = " findItemById" resultType = " com.intellif.mozping.entity.Item" parameterType = " java.lang.Integer" >
SELECT * FROM tb_item where id = #{id};
</ select>
</ mapper>
5.5 测试
注意这里的测试还没有和Java接口文件有任何关系,我们获取sqlsession之后,直接通过 SqlSession来调用xml映射文件里面的sql语句,sqlSession.insert方法的第一个参数就是定位xml文件中sql语句,第二个参数就是调用Sql的传参,因此我们看到其实MyBatis在没有Java接口文件的情况下是可以直接通过映射文件操作数据库的,而这就是ibatis的编程模式,mybatis只是基于此增加了java接口,使得我们可以面向接口编程,在有Java接口文件的情况下无非就是根据接口方法名去找xml中的Sql而已,默认我们在Java接口文件中的方法名和xml中的id是要一致的。
@Data
public class Item {
private int id;
private String description;
}
public class Test01 {
@Test
public void add ( ) throws IOException {
String resource = "mybatis/mybatis-config.xml" ;
InputStream inputStream = Resources. getResourceAsStream ( resource) ;
SqlSession sqlSession = new SqlSessionFactoryBuilder ( ) . build ( inputStream) . openSession ( ) ;
Item item = new Item ( ) ;
item. setId ( 1 ) ;
item. setDescription ( "No 1" ) ;
int rowAffected = sqlSession. insert ( "item.addItem" , item) ;
System. out. println ( "The rows be affected :" + rowAffected) ;
sqlSession. commit ( ) ;
sqlSession. close ( ) ;
}
}
打印:The rows be affected : 1
数据库也有新记录插入。
5.6 优化
SqlSessionFactory在程序生命周期中是单例的,因此可以抽取出来
public class SqlSessionFactoryUtil {
private static SqlSessionFactory factory;
public static SqlSessionFactory getSqlSessionFactoryInstace ( ) {
if ( factory == null) {
InputStream in = null;
try {
in = Resources. getResourceAsStream ( "mybatis/mybatis-config.xml" ) ;
} catch ( Exception e) {
e. printStackTrace ( ) ;
}
synchronized ( SqlSessionFactoryUtil. class ) {
if ( factory == null) {
factory = new SqlSessionFactoryBuilder ( ) . build ( in) ;
}
}
}
return factory;
}
}
5.7 完整增删改查
public class Test01 {
@Test
public void add ( ) {
SqlSession sqlSession = SqlSessionFactoryUtil. getSqlSessionFactoryInstace ( ) . openSession ( ) ;
Item item = new Item ( ) ;
item. setId ( 1 ) ;
item. setDescription ( "No 1" ) ;
int rowAffected = sqlSession. insert ( "item.addItem" , item) ;
System. out. println ( "The rows be affected :" + rowAffected) ;
sqlSession. commit ( ) ;
sqlSession. close ( ) ;
}
@Test
public void query ( ) {
SqlSession sqlSession = SqlSessionFactoryUtil. getSqlSessionFactoryInstace ( ) . openSession ( ) ;
List< Object> list = sqlSession. selectList ( "item.findItemById" , 1 ) ;
for ( Object obj : list) {
System. out. println ( ( ( Item) obj) . toString ( ) ) ;
}
}
@Test
public void update ( ) {
SqlSession sqlSession = SqlSessionFactoryUtil. getSqlSessionFactoryInstace ( ) . openSession ( ) ;
Item item = new Item ( ) ;
item. setId ( 1 ) ;
item. setDescription ( "No 1 be updated" ) ;
int rowAffected = sqlSession. update ( "item.updateItemById" , item) ;
System. out. println ( "The rows be affected :" + rowAffected) ;
sqlSession. commit ( ) ;
sqlSession. close ( ) ;
}
@Test
public void delete ( ) {
SqlSession sqlSession = SqlSessionFactoryUtil. getSqlSessionFactoryInstace ( ) . openSession ( ) ;
int rowAffected = sqlSession. delete ( "item.deleteItemById" , 1 ) ;
System. out. println ( "The rows be affected :" + rowAffected) ;
sqlSession. commit ( ) ;
sqlSession. close ( ) ;
}
}
5.8 使用代理的方式访问
在之前我们小结了,我们没有和Java接口有任何的关系,我们直接就是使用Mybatis加载它的主配置文件,在主配置文件中配置了xml映射文件,然后获得了sqlsession之后,我们就可以通过xml中的id和namespace来访问调用里面的sql语句了。相当于是sqlsession —> xml —> DB 但是我们一般是通过Java接口来使用的,而且通常我们在Java中的接口名字和xml中的id是保持一致的,这个过程是如何实现的,具体深层原理我们暂时无法捋清楚,但是底层其实是使用了动态代理,这里手动使用动态代理来做一次数据库的访问(如果我们做到了通过接口访问数据库,里面使用动态代理技术,那么实际上这里看起来就像是已经做到了mybatis框架所做的事情,而mybatis实际上就是这么干的)。
5.8.1 创建Java接口
public interface ItemMapper {
int addItem ( Item item) ;
int deleteItemById ( int id) ;
int updateItemById ( Item item) ;
List< Object> findItemById ( int id) ;
}
5.8.2 通过Java接口访问数据库
@Test
public void addInterface ( ) {
SqlSession sqlSession = SqlSessionFactoryUtil. getSqlSessionFactoryInstace ( ) . openSession ( ) ;
Item item = new Item ( ) ;
item. setId ( 1 ) ;
item. setDescription ( "No 1" ) ;
ItemMapper mapper = sqlSession. getMapper ( ItemMapper. class ) ;
int rowAffected = mapper. addItem ( item) ;
System. out. println ( "The rows be affected :" + rowAffected) ;
sqlSession. commit ( ) ;
sqlSession. close ( ) ;
}
验证保存数据成功,但是这里注意需要将xml中的命名空间修改一下,前面是item,这里需要修改为ItemMapper的全类名:
com. intellif. mozping. dao. ItemMapper
5.8.3 自己通过代理方式
下面代码中,我们通过动态代理生产一个ItemMapper接口对象,这个对象调用addItem方法,但是实际上底层,我们是通过动态代理 对象来真正的访问数据库的,我们获得了mapper.addItem这个方法的全限定名称,实际上就是xml文件中的namespace+id,通过这个来调用,是不是又回到了之前的sqlsession的最原始的不需要Java接口的工作方式,调用方式和前面的add都是一模一样的,这样我们就可以简单的通过接口来访问数据库了。而实际上MyBatis框架就是按照这样的思想来做的,程序员面向接口编程,调用接口中的方法,底层通过动态代理创建了代理对象去真正的访问数据库,通过接口方法Mybatis会知道你想调用xml中的哪一个sql,然后让代理对象去做,底层还是sqlsession在做一系列的事情,但是却简化了编程的工作,对于单纯的使用,我们甚至可以不care sqlsession的存在。
@Test
public void proxyMybatis ( ) {
ItemMapper mapper = ( ItemMapper) Proxy. newProxyInstance ( ItemMapper. class . getClassLoader ( )
, new Class [ ] { ItemMapper. class } , new InvocationHandler ( ) {
@Override
public Object invoke ( Object proxy, Method method, Object[ ] args) throws Throwable {
System. out. println ( ItemMapper. class . getName ( ) + "." + method. getName ( ) ) ;
Object obj = null;
for ( Object object : args) {
System. out. println ( object) ;
obj = object;
}
SqlSession sqlSession = SqlSessionFactoryUtil. getSqlSessionFactoryInstace ( ) . openSession ( ) ;
int result = sqlSession. insert ( ItemMapper. class . getName ( ) + "." + method. getName ( ) , obj) ;
sqlSession. commit ( ) ;
sqlSession. close ( ) ;
return result;
}
} ) ;
Item item = new Item ( ) ;
item. setId ( 1 ) ;
item. setDescription ( "No 1" ) ;
int rowAffected = mapper. addItem ( item) ;
}
对比动态代理方式的实现代码和通过Java接口访问数据库的代码,我们完全可以把创建代理对象的代码和下面的代码等价起来 ,我们看到前后的代码都是一致的,我们可以这样理解sqlSession.getMapper(ItemMapper.class)其实就是创建了一个ItemMapper 类型的代理对象,后期通过mapper.addItem的方式访问数据库,其实都是代理对象去做的,代理对象通过方法名找到xml中的sql来执行。如果再结合Spring,通过注解实现代理对象的创建并交给IOC容器去管理,那么将大大简化代码。
ItemMapper mapper = sqlSession.getMapper(ItemMapper.class);
5.8.4 注意事项
在使用Java接口的方式访问数据库我们也发现了几点需要注意的地方
序号
注意点
1
接口方法的全限定名称必须和xml中的namespace+id保持一致,接口类名对应namespace,方法名对应id
2
映射文件的名称必须和接口的名称一致,映射文件是XXX.xml,接口文件是XXX.java
六、小结
我们首先通过简单的入门案例了解了mybatis的使用,在测试代码中我们用ibatis的编程方式来访问数据库,然后使用动态代理实现了使用接口访问数据库,而mybatis做 的工作就是基于ibatis的方式,让程序员可以面向接口编程,其底层也是使用动态代理来实现,这些细节我们在后循序渐进的学习。