1. 简介
Java程序都是通过JDBC连接数据库,但是只定义了接口规范,具体的实现交给各个数据库厂商去实现,因为每个数据库都有其特殊性。所以JDBC是一种桥接模式。
这里为什么说JDBC是一种桥接模式呢?为例避免JDBC的缺陷出现了ORM模型,比如Hibernate,提供的是一种全表映射模型。因为Hibernate的一些缺陷,比如:
- Hibernate屏蔽了SQL,只能全表映射,更新时需要发送所有的字段
- 无法根据不同的条件组装不同的SQL
- 对多表关联和复杂SQL查询支持较差,需要自己写SQL,返回后,需要自己将数据组装为POJO
- 不能有效地支持存储过程
- 虽然有HQL,但是性能较差。
因此出现了MyBatis,一个半自动的框架。之所以称之为半自动,是因为它需要手动提供POJO、SQL和映射关系,而全表映射的Hibernate只需要提供POJO和映射关系即可,因为Hibernate无需编写SQL,所以开发效率优于MyBatis。
2. MyBatis入门
MyBatis的核心组件:
- SqlSessionFactoryBuilder(构造器):它会根据配置信息或者代码来生成SqlSessionFactory(工厂接口)
- SqlSessionFactory:依靠工厂来生成SqlSession
- SqlSession:是一个既可以发送SQL去执行并返回结果,也可以获取Mapper的接口
- SQL Mapper:它是MyBatis新设计的组件,是由一个java接口和XML文件(或注解)构成的,需要给出对应的SQL和映射规则。它负责发送SQL去执行,并返回结果。
2.1 构建SqlSessionFactory
每个MyBatis的应用都是以SqlSessionFactory的实例为中心,可以通过SqlSessionFactoryBuilder获得,注意SqlSessionFactory是一个工厂接口而不是实现类,其任务是创建SqlSession。SqlSession类似于一个JDBC的Connection对象。MyBatis提供了两种模式去创建SqlSessionFactory:一种是XML配置的方式,另一种是代码方式。要尽量的使用配置文件,这样一方面可以避免硬编码,一方面方便日后配置人员修改。
org.apache.ibatis.session.Configuration在MyBatis中将以一个Configuration类对象的形式存在,存在于整个MyBatis应用的生命期中,以便重复读取和运用。我们可以一次性解析一次配置的XML保存到Configuration对象中。在MyBatis中提供了两个SqlSessionFactory的实现类,DefaultSqlSessionFactory和SqlSessionManager。目前使用的是DefaultSqlSessionFactory。
2.1.1 使用XML方式构建
这里以一个简易的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> <!--定义别名 --> <typeAliases> <typeAlias alias="role" type="com.learn.chapter2.po.Role"/> </typeAliases> <!--定义数据库信息,默认使用development数据库构建环境--> <environments default="development"> <environment id="development"> <!--采用jdbc事务管理 --> <transactionManager type="JDBC"> <property name="autoCommit" value="false"/> </transactionManager> <!--配置数据库链接信息 --> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/mybatis" /> <property name="username" value="root" /> <property name="password" value="root" /> </dataSource> </environment> </environments> <!--定义映射器 --> <mappers> <mapper resource="com/learn/chapter2/mapper/roleMapper.xml"/> </mappers> </configuration>
然后用代码实现创建SqlSessionFactory:
String resource="mybatis-config.xml"; InputStream inputStream=null; inputStream=Resources.getResourceAsStream(resource); sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
这里创建了一个XML文件输入流,用SqlSessionFactoryBuilder读取XML的信息 来创建SqlSessionFactory的对象。MyBatis的解析程序会将mybatis-config.xml文件配置信息解析到Configuration类对象里面,然后利用SqlSessionFactoryBuilder读取这个对象,为我们创建SqlSessionFactory。
2.1.2 使用代码方式构建
不推荐这种方式,因为修改环境的时候,要重新编译代码,不利于维护。
//构建数据库连接池 PooledDataSource dataSource=new PooledDataSource(); dataSource.setDriver("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/mybatis"); dataSource.setUsername("root"); dataSource.setPassword("root"); //构建数据库事务方式 TransactionFactory transactionFactory=new JdbcTransactionFactory(); //创建数据库运行环境 Environment environment=new Environment("development",transactionFactory,dataSource); //构建Configuration对象 Configuration configuration=new Configuration(environment); //注册一个MyBatis上下文别名 configuration.getTypeAliasRegistry().registerAlias("role",Role.class); //加入一个映射器 configuration.addMapper(RoleMapper.class); //使用SqlSessionFactoryBuilder构建SqlSessionFactory SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(configuration); return sqlSessionFactory;
采用代码方式一般是需要加入自己特性的时候才会用到,例如数据源配置的信息要求是加密的时候,我们需要把它转化出来。大部分情况下不建议用这个方式来创建SqlSessionFactory
2.1.2 创建SqlSession
在MyBatis中SqlSession接口的实现类有两个,分别是DefaultSqlSession和SqlSessionManager。我们构建了SqlSessionFactory,然后生成门面接口SqlSession。SqlSession接口类似于一个JDBC中的Connection接口对象,我们需要保证每次用完正确关闭它。
SqlSession的用途主要是获取映射器以及直接通过命名信息去执行SQL返回结果。
2.1.3 映射器
由Java接口和XML文件(或注解)共同组成,作用如下:
- 定义参数类型
- 描述缓存
- 描述SQL语句
- 定义查询结果和POJO的映射关系
使用XML文件配置方式实现Mapper,第一步给出Java接口:
public interface RoleMapper { public Role getRole(Long id); public int deleteRole(Long id); public int insertRole(Role role); }
第二步给出一个映射文件:
<?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="com.learn.chapter2.mapper.RoleMapper"> <select id="getRole" resultType="role" parameterType="long"> select id,role_name as roleName,note from t_role where id=#{id} </select> </mapper>
- MyBatis会读取这个配置文件,生成映射器
- 定义一个命名空间为com.learn.chapter2.mapper.RoleMapper的SQL Mapper,这个命名空间和我们定义的接口的全限定名要一致
- 用一个select元素定义一个查询SQL,id是getRole,和我们接口中的方法名是一致的
- #{id}为这条SQL的参数。而SQL列的别名和POJO的属性名称保持一致。那么MyBatis就会把这条语句查询的结果自动映射到我们需要的POJO属性上
现在可以使用SqlSession来获取这个Mapper:
RoleMapper roleMapper=sqlSession.getMapper(RoleMapper.class); Role role=roleMapper.getRole(1L);
2.1.4 Java注解方式实现Mapper
不推荐注解的方式,如果系统简单的话,使用注解方式也不失为一个好办法。
注册这个接口为映射器:
2.1.4 一些疑问
这要注意的是在MyBatis中保留这iBatis,通过命名空间+SQL id 的方式发送SQL并返回数据的形式,而不需要获取映射器:
2.2 生命周期
SqlSessionFactoryBuilder:只存在于方法的局部,作用是生成SessionFactory对象。
SqlSessionFactory:存在于MyBatis应用的整个生命周期中,唯一责任是创建SqlSession,要采用单例模式。
SqlSession:一个会话,相当于JDBC的一个Connection对象,他的生命周期应该是在请求数据库处理事务的过程中。它是一个线程不安全的对象。
Mapper:一个接口,没有任何实现类,作用是发送SQL。因此它应该在一个SqlSession事务方法之内,是一个方法级别的东西。
2.3 实例
代码清单2-11:log4j.properties
log4j.rootLogger=DEBUG, stdout log4j.logger.org=BEBUG log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%5p %d %C: m%n
其中参数的解释见博文:http://www.cnblogs.com/alipayhutu/archive/2012/06/21/2558249.html
其次,构建SessionFactory,我们需要配置文件,代码:
<?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> <!--定义别名 --> <typeAliases> <typeAlias alias="role" type="com.learn.chapter2.po.Role"/> </typeAliases> <!--定义数据库信息,默认使用development数据库构建环境--> <environments default="development"> <environment id="development"> <!--采用jdbc事务管理 --> <transactionManager type="JDBC"> <property name="autoCommit" value="false"/> </transactionManager> <!--配置数据库链接信息 --> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/mybatis" /> <property name="username" value="root" /> <property name="password" value="root" /> </dataSource> </environment> </environments> <!--定义映射器 --> <mappers> <mapper resource="com/learn/chapter2/mapper/roleMapper.xml"/> </mappers> </configuration>
构建SqlSessionFactory
package com.learn.chapter2.util; import java.io.InputStream; import java.util.logging.Level; import java.util.logging.Logger; import org.apache.commons.logging.Log; 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 SqlSessionFactoryUtil { //SqlSessionFactory对象 private static SqlSessionFactory sqlSessionFactory=null; //类线程锁 private static final Class Class_LOCK=SqlSessionFactoryUtil.class; /** * 私有化构造参数 */ private SqlSessionFactoryUtil(){} /** * 构建SqlSessionFactory */ public static SqlSessionFactory initSqlSessionFactory(){ String resource="mybatis-config.xml"; InputStream inputStream=null; try{ inputStream=Resources.getResourceAsStream(resource); }catch (Exception e) { Logger.getLogger(SqlSessionFactoryUtil.class.getName()).log(Level.SEVERE, null,e); } synchronized (Class_LOCK) { if(sqlSessionFactory==null){ sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream); } } return sqlSessionFactory; } /** * 打开SqlSession */ public static SqlSession openSqlSession(){ if(sqlSessionFactory==null){ initSqlSessionFactory(); } return sqlSessionFactory.openSession(); } }
上面要注意的部分:
1. 类线程锁:private static final Class Class_LOCK=SqlSessionFactoryUtil.class;
实现一个POJO类——Role.java
package com.learn.chapter2.po; public class Role { private Long id; private String roleName; private String note; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getRoleName() { return roleName; } public void setRoleName(String roleName) { this.roleName = roleName; } public String getNote() { return note; } public void setNote(String note) { this.note = note; } }
还需要一个映射器的描述:
<?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="com.learn.chapter2.mapper.RoleMapper"> <select id="getRole" resultType="role" parameterType="long"> select id,role_name as roleName,note from t_role where id=#{id} </select> <insert id="insertRole" parameterType="role"> insert into t_role(role_name,note) values (#{roleName},#{note}) </insert> <delete id="deleteRole" parameterType="long"> delete from t_role where id=#{id} </delete> </mapper>
定义相应的接口:
package com.learn.chapter2.mapper; import com.learn.chapter2.po.Role; public interface RoleMapper { public Role getRole(Long id); public int deleteRole(Long id); public int insertRole(Role role); }
使用一个main方法来测试:
package com.learn.chapter2.main; import org.apache.ibatis.session.SqlSession; import com.learn.chapter2.mapper.RoleMapper; import com.learn.chapter2.po.Role; import com.learn.chapter2.util.SqlSessionFactoryUtil; public class Chapter2Main { public static void main(String[] args){ SqlSession sqlSession=null; try{ sqlSession=SqlSessionFactoryUtil.openSqlSession(); RoleMapper roleMapper=sqlSession.getMapper(RoleMapper.class); Role role=new Role(); role.setRoleName("testName"); role.setNote("testRole"); roleMapper.insertRole(role); roleMapper.deleteRole(1L); sqlSession.commit(); }catch (Exception e) { System.err.println(e.getMessage()); sqlSession.rollback(); }finally{ if(sqlSession!=null){ sqlSession.close(); } } } }