MyBatis入门
什么是MyBatis
MyBatis是一款优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射。MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。
MyBatis可以使用简单的xml或注解来配置和映射原生类型、接口和Java POJO为数据库中的记录。具体可以参考MyBatis官网:https://mybatis.org/mybatis-3/zh/index.html
使用MyBatis
1、引入mybatis依赖
要使用 MyBatis,只需要将mybatis的jar包置于classpath中即可。
如果使用Maven来构建项目,则需在pom.xml文件中引入mybatis的依赖:
1 <dependency> 2 <groupId>org.mybatis</groupId> 3 <artifactId>mybatis</artifactId> 4 <version>版本号</version> 5 </dependency>
我们在这里使用Maven构建项目。
2、从xml中构建SqlSessionFactory
每个基于MyBatis的应用都是以一个SqlSessionFactory的实例为核心的,SqlSessionFactory的实例可以通过SqlSessionFactoryBuilder 获得。而SqlSessionFactoryBuilder 可以从xml配置文件或一个预先定制的Configuration的实例构建出SqlSessionFactory的实例。
(1)从xml中构建SqlSessionFactory
从 XML 文件中构建 SqlSessionFactory 的实例非常简单,建议使用类路径下的资源文件进行配置。但是也可以使用任意的输入流(InputStream)实例,包括字符串形式的文件路径或者 file:// 的 URL 形式的文件路径来配置。MyBatis 包含一个名叫 Resources 的工具类,它包含一些实用方法,可使从 classpath 或其他位置加载资源文件更加容易。
1 package com.ly.demo.mybatis.simple; 2 3 import org.apache.ibatis.io.Resources; 4 import org.apache.ibatis.session.SqlSessionFactory; 5 import org.apache.ibatis.session.SqlSessionFactoryBuilder; 6 7 import java.io.IOException; 8 import java.io.InputStream; 9 10 public class SimpleMyBatisTest { 11 public void buildSqlSessionFactoryByXml() throws IOException { 12 // mybatis-config.xml文件位于resources下的mybatis目录下 13 InputStream is = Resources.getResourceAsStream("mybatis/mybatis-config.xml"); 14 SqlSessionFactory sqlSessionFactory1 = new SqlSessionFactoryBuilder().build(is); 15 } 16 }
XML 配置文件中包含了对 MyBatis 系统的核心设置,包含获取数据库连接实例的数据源(DataSource)和决定事务作用域和控制方式的事务管理器(TransactionManager)。XML 配置文件的详细内容后面再探讨,这里先给出一个简单的示例:
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <!DOCTYPE configuration 3 PUBLIC "-//mybatis.org//DTD Config 3.0//EN" 4 "http://mybatis.org/dtd/mybatis-3-config.dtd"> 5 <configuration> 6 <!-- MyBatis可以配置成适应多种环境,这种机制有助于SQL映射应用于多种数据库之中, 7 现实情况下有多种理由需要这么做。例如,开发、测试和生产环境需要有不同的配置; 8 或者想在具有相同 Schema 的多个生产数据库中 使用相同的 SQL 映射。有许多类似的使用场景。 9 不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。 10 所以,如果你想连接两个数据库,就需要创建两个 SqlSessionFactory 实例,每个数据库对应一个。而如果是三个数据库,就需要三个实例,依此类推。 11 // 不传第二个参数使用default属性对应的环境 12 SqlSessionFactory sqlSessionFactory1 = new SqlSessionFactoryBuilder().build(is); 13 SqlSessionFactory sqlSessionFactory2 = new SqlSessionFactoryBuilder().build(is, "development"); 14 --> 15 <environments default="development"> 16 <environment id="development"> 17 <!-- 事物管理器的配置 18 在MyBatis中有两种类型的事物管理器,即type="JDBC|MANAGED", 19 JDBC - 直接使用JDBC的提交和回滚设置,依赖于从数据源得到的连接来管理事物作用域。 20 MANAGED - 这个配置几乎没做什么。它从来不提交或者回滚一个连接,而是让容器来管理事物的整个生命周期(比如 JEE 应用服务器的上下文), 21 默认情况下它会关闭连接,然而一些容器并不希望这么做,因此需要将closeConnection属性设置为false 22 来阻止它默认的关闭行为。例如: 23 <transactionManager type="MANAGED"> 24 <property name="closeConnection" value="false"/> 25 </transactionManager> 26 --> 27 <transactionManager type="JDBC"/> 28 <!-- 数据源的配置 29 有三种内建的数据源类型(也就是 type=”[UNPOOLED|POOLED|JNDI]”): 30 UNPOOLED – 每次被请求时打开和关闭连接。 31 POOLED – 利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 32 这是一种使得并发 Web 应用快速响应请求的流行处理方式。 33 JNDI – 为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。 34 --> 35 <dataSource type="POOLED"> 36 <property name="driver" value="${driver}"/> 37 <property name="url" value="${url}"/> 38 <property name="username" value="${username}"/> 39 <property name="password" value="${password}"/> 40 </dataSource> 41 </environment> 42 </environments> 43 44 <mappers> 45 <!-- 告诉MyBatis去哪里找SQL 映射语句 --> 46 <mapper resource="mybatis/mapper/EmployeeMapper.xml"/> 47 </mappers> 48 </configuration>
当然,还有很多可以在 XML 文件中进行配置,上面的示例指出的则是最关键的部分。 要注意 XML 头部的声明,它用来验证 XML 文档正确性。environment 元素体中包含了事务管理和连接池的配置。mappers 元素则是包含一组映射器(mapper),这些映射器的 XML 映射文件包含了 SQL 代码和映射定义信息。
(2)Java代码构建SqlSessionFactory
如果你更愿意直接从 Java 代码而不是 XML 文件中创建配置,或者想要创建你自己的配置构建器,MyBatis 也提供了完整的配置类,提供所有和 XML 文件相同功能的配置项。
1 package com.ly.demo.mybatis.simple; 2 3 import com.ly.demo.mybatis.simple.mapper.EmployeeMapper; 4 import com.mysql.cj.jdbc.MysqlConnectionPoolDataSource; 5 import org.apache.ibatis.mapping.Environment; 6 import org.apache.ibatis.session.Configuration; 7 import org.apache.ibatis.session.SqlSessionFactory; 8 import org.apache.ibatis.session.SqlSessionFactoryBuilder; 9 import org.apache.ibatis.transaction.TransactionFactory; 10 import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory; 11 12 import javax.sql.DataSource; 13 14 public class SimpleMyBatisTest { 15 16 private void buildSqlSessionFactoryByJavaCode() { 17 DataSource dataSource = new MysqlConnectionPoolDataSource(); 18 TransactionFactory transactionFactory = new JdbcTransactionFactory(); 19 Environment environment = new Environment("development", transactionFactory, dataSource); 20 Configuration configuration = new Configuration(environment); 21 configuration.addMapper(EmployeeMapper.class); 22 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration); 23 } 24 }
注意该例中,configuration 添加了一个映射器类(mapper class)。映射器类是 Java 类,它们包含 SQL 映射语句的注解从而避免依赖 XML 文件。不过,由于 Java 注解的一些限制以及某些 MyBatis 映射的复杂性,要使用大多数高级映射(比如:嵌套联合映射),仍然需要使用 XML 配置。有鉴于此,如果存在一个同名 XML 配置文件,MyBatis 会自动查找并加载它(在这个例子中,基于类路径和 EmployeeMapper.class 的类名,会加载 EmployeeMapper.xml)。具体细节稍后讨论。
3、从SqlSessionFactory中获取SqlSession
既然有了 SqlSessionFactory,顾名思义,我们就可以从中获得 SqlSession 的实例了。SqlSession 完全包含了面向数据库执行 SQL 命令所需的所有方法。你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。
1 SqlSession sqlSession = sqlSessionFactory1.openSession(); 2 Employee employee = sqlSession.selectOne("com.ly.demo.mybatis.simple.mapper.EmployeeMapper.selectOne", 1);
诚然,这种方式能够正常工作,并且对于使用旧版本 MyBatis 的用户来说也比较熟悉。不过现在有了一种更简洁的方式 ——使用正确描述每个语句的参数和返回值的接口(比如 BlogMapper.class),我们现在不仅可以执行更清晰和类型安全的代码,而且还不用担心易错的字符串字面值以及强制类型转换。例如:
1 EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class); 2 Employee employee = mapper.selectOne(1);
4、探究已映射的SQL语句
在上面提到的例子中,一个语句既可以通过 XML 定义,也可以通过注解定义。我们先看看 XML 定义语句的方式。事实上 MyBatis 提供的全部特性都可以利用基于 XML 的映射语言来实现,这使得 MyBatis 在过去的数年间得以流行。如果你以前用过 MyBatis,你应该对这个概念比较熟悉。 不过自那以后,XML 的配置也改进了许多,我们稍后还会再提到。这里给出一个基于 XML 映射语句的示例,它应该可以满足上述示例中 SqlSession 的调用。
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <!DOCTYPE mapper 3 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 4 "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 5 6 <mapper namespace="com.ly.demo.mybatis.simple.mapper.EmployeeMapper"> 7 <select id="selectOne" resultType="Employee"> 8 select * from employee where id = #{id} 9 </select> 10 </mapper>
为了这个简单的例子,我们似乎写了不少配置,但实际上它并不多。在一个 XML 映射文件中,可以定义无数个映射语句,这样一来,XML 头部和文档类型声明占去的部分就显得微不足道了。而文件的剩余部分具备自解释性,很容易理解。 在命名空间 “com.ly.demo.mybatis.simple.mapper.EmployeeMapper” 中定义了一个名为 “selectOne” 的映射语句,允许你使用指定的完全限定名 “com.ly.demo.mybatis.simple.mapper.EmployeeMapper.selectOne” 来调用映射语句,就像上面例子中那样:
1 SqlSession sqlSession = sqlSessionFactory1.openSession(); 2 Employee employee = sqlSession.selectOne("com.ly.demo.mybatis.simple.mapper.EmployeeMapper.selectOne", 1);
你可能注意到这和使用完全限定名调用 Java 对象的方法类似。这样,该命名就可以直接映射到在命名空间中同名的 Mapper 类,并将已映射的 select 语句中的名字、参数和返回类型匹配成方法。 因此你就可以像上面那样很容易地调用这个对应 Mapper 接口的方法,就像下面这样:
1 SqlSession sqlSession = sqlSessionFactory1.openSession(); 2 EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class); 3 Employee employee = mapper.selectOne(1);
tips1:对命名空间的疑点说明
在之前版本的 MyBatis 中,命名空间(Namespaces)的作用并不大,是可选的。 但现在,随着命名空间越发重要,你必须指定命名空间。
命名空间的作用有两个,一个是利用更长的完全限定名来将不同的语句隔离开来,同时也实现了我们上面见到的接口绑定。就算你觉得暂时用不到接口绑定,我们也应该遵循这里的规定,以防哪天我们改变了主意。 长远来看,只要将命名空间置于合适的 Java 包命名空间之中,我们的代码会变得更加整洁,也有利于我们更方便地使用 MyBatis。
命名解析:为了减少输入量,MyBatis 对所有的命名配置元素(包括语句,结果映射,缓存等)使用了如下的命名解析规则。
- 完全限定名(比如 “com.mypackage.MyMapper.selectAllThings)将被直接用于查找及使用。
- 短名称(比如 “selectAllThings”)如果全局唯一也可以作为一个单独的引用。 如果不唯一,有两个或两个以上的相同名称(比如 “com.foo.selectAllThings” 和 “com.bar.selectAllThings”),那么使用时就会产生“短名称不唯一”的错误,这种情况下就必须使用完全限定名。
对于像 EmployeeMapper 这样的映射器类来说,还有另一种方法来处理映射。 它们映射的语句可以不用 XML 来配置,而可以使用 Java 注解来配置。比如,上面的 XML 示例可被替换如下:
1 package com.ly.demo.mybatis.simple.mapper; 2 3 import com.ly.demo.mybatis.simple.entity.Employee; 4 import org.apache.ibatis.annotations.Select; 5 6 public interface EmployeeMapper { 7 @Select("SELECT * FROM EMPLOYEE WHERE ID = #{id}") 8 Employee selectOne(int id); 9 }
使用注解来映射简单语句会使代码显得更加简洁,然而对于稍微复杂一点的语句,Java 注解就力不从心了,并且会显得更加混乱。 因此,如果我们需要完成很复杂的事情,那么最好使用 XML 来映射语句。
选择何种方式来配置映射,以及认为映射语句定义的一致性是否重要,这些完全取决于你和你的团队。 换句话说,永远不要拘泥于一种方式,我们可以很轻松的在基于注解和 XML 的语句映射方式间自由移植和切换。
tips2:作用域(Scope)和生命周期
对象生命周期和依赖注入框架
依赖注入框架可以创建线程安全的、基于事务的 SqlSession 和映射器,并将它们直接注入到你的 bean 中,因此可以直接忽略它们的生命周期。 如果对如何通过依赖注入框架来使用 MyBatis 感兴趣,可以研究一下 MyBatis-Spring 或 MyBatis-Guice 两个子项目。
SqlSessionFactoryBuilder
SqlSessionFactoryBuilder可以被实例化、使用和丢弃,一旦创建了SqlSessionFactory,就不再需要SqlSessionFactoryBuilder了。因此,SqlSessionFactoryBuilder实例的最佳作用域是方法作用域(也就是局部方法变量)。我们可以重用SqlSessionFactoryBuilder来创建多个SqlSessionFactory实例,但最好还是不要让其一直存在,以保证所有的XML解析资源可以被释放给更重要的事情。
SqlSessionFactory
SqlSessionFactory一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。使用SqlSessionFactory的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏味道(bad smell)”。因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。
SqlSession
每个线程都应该有它自己的SqlSession实例,SqlSession的实例不是线程安全的,因此是不能被共享的,因此它的最佳作用域是请求或方法作用域。绝对不能将SqlSession的实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。如果你现在正在使用一种 Web 框架,要考虑 SqlSession 放在一个和 HTTP 请求对象相似的作用域中。换句话说,每次收到的 HTTP 请求,就可以打开一个 SqlSession,返回一个响应,就关闭它。 这个关闭操作是很重要的,你应该把这个关闭操作放到 finally 块中以确保每次都能执行关闭。 下面的示例就是一个确保 SqlSession 关闭的标准模式:
1 SqlSession sqlSession = null; 2 try { 3 sqlSession = sqlSessionFactory.openSession(); 4 // 逻辑代码 5 } finally { 6 if (sqlSession != null) { 7 sqlSession.close(); 8 } 9 }
在你的所有的代码中一致地使用这种模式来保证所有数据库资源都能被正确地关闭。
映射器实例
映射器是一些由你创建的、绑定你映射的语句的接口(即我们自己创建的那些Mapper接口)。映射器接口的实例是从 SqlSession 中获得的。因此从技术层面讲,任何映射器实例的最大作用域是和请求它们的 SqlSession 相同的。尽管如此,映射器实例的最佳作用域是方法作用域。也就是说,映射器实例应该在调用它们的方法中被请求,用过之后即可丢弃。 并不需要显式地关闭映射器实例,尽管在整个请求作用域保持映射器实例也不会有什么问题,但是你很快会发现,像 SqlSession 一样,在这个作用域上管理太多的资源的话会难于控制。 为了避免这种复杂性,最好把映射器放在方法作用域内。下面的示例就展示了这个实践:
1 SqlSession sqlSession = null; 2 try { 3 sqlSession = sqlSessionFactory.openSession(); 4 EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class); 5 // 逻辑代码 6 } finally { 7 if (sqlSession != null) { 8 sqlSession.close(); 9 } 10 }

浙公网安备 33010602011771号