Mybatis的核心组件及其生命周期#
SqlSessionFactoryBuider:#
作用: 构建器,根据配置信息生成SqlSessionFactory
生命周期: 这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。 你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但是最好还是不要让其一直存在,以保证所有的 XML 解析资源可以被释放给更重要的事情。
SqlSessionFactory#
作用: 生成sqlSession
生命周期: SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“bad smell”。因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式
SqlSession#
作用: 它表示一次sql的会话,即可以去执行sql返回结果,也可以获取为mapper生成的代理对象 ,支持事物,通过commit、rollback方法提交或者回滚事物
生命周期: 每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你现在正在使用一种 Web 框架,要考虑 SqlSession 放在一个和 HTTP 请求对象相似的作用域中。 换句话说,每次收到的 HTTP 请求,就可以打开一个 SqlSession,返回一个响应,就关闭它。 这个关闭操作是很重要的,你应该把这个关闭操作放到 finally 块中以确保每次都能执行关闭。
SqlMapper#
作用: : MyBatis的映射器,现在大多数都使用java接口,早前使用配置文件来描述sql查询结果和java对象之间的映射规则 定义参数类型, 描述缓存,描述SQL语句 ,定义查询结果和POJO的映射关系
生命周期: 最好把映射器放在方法作用域内
基于XML版本的环境搭建测试#
基于xml版本,搭建mybatis开发环境中,存在一个主配置文件,和多个子配置文件,主配置文件中配置数据库相关的信息, 而子配置文件中配置的是单个Dao接口层中的抽象方法对应的sql语句
主配置文件如下
需要注意的地方,下面的<mapper >
标签中resource
属性存放的是从配置文件的路径,但是从配置文件的目录信息得和src中相应的接口位于相同的目录
<?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"> <!--mybatis的主配置文件--> <configuration> <!--配置环境--> <environments default="mysql"> <!--配置mysql的环境--> <environment id="mysql"> <!--配置事务的类型--> <transactionManager type="JDBC"/> <!--配置数据源--> <!--dataSource存在三个, 其中的POOLED池化的连接池--> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/trymybatis"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> <!--指定映射配置文件的位置,也就是针对每个Dao的配置文件的位置--> <!--下面指定的xml配置文件的路径,需要和src下IUserDao接口的目录保持一致--> <mappers> <mapper resource="com/changwu/dao/IUserDao.xml"/> </mappers> </configuration>
从配置文件
需要注意的地方: 命名空间是全类名,id是方法名,返回值是全类名
还有一点就是,单个mapper标签中,namespace和id都不能少,两者合在一起才能确定一个全局唯一的方法,至于为什么我们配置一个接口就ok,而不用添加配置文件,那是因为Mybatis会使用代理技术,为接口生成代理对象,让程序员使用代理对象
<?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 namespace="com.changwu.dao.IUserDao"> <!-- id为方法名 resultType为返回值个体的封装类 --> <select id="findAll" resultType="com.changwu.pojo.User"> select * from user </select> </mapper>
其他数据源的配置方式
把如下的配置放在resourse目录下面
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/trymybatis jdbc.username=root jdbc.password=root
然后将主配置文件改成下面这样
当然也可以在<properties>
标签中使用url
但是需要遵循url协议的规范
<?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"> <!--mybatis的主配置文件--> <configuration> <properties resource="jdbcConfig.properties"> </properties> <!--配置环境--> <environments default="mysql"> <!--配置mysql的环境--> <environment id="mysql"> <!--配置事务的类型--> <transactionManager type="JDBC"/> <!--配置数据源--> <!--dataSource存在三个, 其中的POOLED池化的连接池--> <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> <!--指定映射配置文件的位置,也就是针对每个Dao的配置文件的位置--> <!--下面指定的xml配置文件的路径,需要和src下IUserDao接口的目录保持一致--> <mappers> <mapper class="com.changwu.dao.IUserDao"/> </mappers> </configuration>
编码测试类
@Test public void text01() { try { // 1. 读取配置文件 InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml"); // 2. 创建SqlSessionFactory工厂 SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(resourceAsStream); // 3. 创建sqlSession SqlSession sqlSession = factory.openSession(); // 4. SqlSession 完全包含了面向数据库执行 SQL 命令所需的所有方法 // 使用正确描述每个语句的参数和返回值的接口(比如 BlogMapper.class), // 你现在不仅可以执行更清晰和类型安全的代码,而且还不用担心易错的字符串字面值以及强制类型转换 IUserDao userDao = sqlSession.getMapper(IUserDao.class); // 5. 执行方法 List<User> all = userDao.findAll(); for (User user : all) { System.out.println(user.getUsername()); } // 6, 释放资源 sqlSession.close(); resourceAsStream.close(); } catch (Exception e) { e.printStackTrace(); } }
基于注解版本的环境搭建测试#
因为现在依然是MyBatis孤军深入,没有和Spring,Springboot等框架进行整合,因此上面说的那个主配置文件无论如果都不能缺失,不像SpringBoot那样一个@MapperScan("XXX")
就完成扫描整合这么给力
基于注解的开发模式,我们可以轻易进一步去除单个dao层的接口对应的xml配置文件,取代之的是注解,三步:
- 第一步: 删除原来的子配置文件的目录
- 第二步: 在dao层接口使用注解开发
@Select("select * from user") List<User> findAll();
- 第三步: 修改部分主配置文件
<mappers> <mapper class="com.changwu.dao.IUserDao"/> </mappers>
常用的注解#
- @Results
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Results { // 因为当前的Results注解中存在实例的描述,使用id标识当前的map,实现给 @resultMap 的复用 String id() default ""; Result[] value() default {}; }
- @Result
继续看看这个@Result
注解,如下: 这个注解拥有xml中的resultMap
中大部分的属性
@Documented @Retention(RetentionPolicy.RUNTIME) @Target({}) public @interface Result { boolean id() default false; // 表中的列名 String column() default ""; // java实体类中的属性名 String property() default ""; // 实体类型 Class<?> javaType() default void.class; JdbcType jdbcType() default JdbcType.UNDEFINED; Class<? extends TypeHandler> typeHandler() default UnknownTypeHandler.class; // 实体之间的关系为1对1 One one() default @One; // 实体之间的关系为1对多 Many many() default @Many; }
- @One
跟进@One
注解, 他是对select
属性的封装, FetchType
是一个枚举,三种值,分别是LAZY, EAGER, DEFAULT
@Documented @Retention(RetentionPolicy.RUNTIME) @Target({}) public @interface One { String select() default ""; FetchType fetchType() default FetchType.DEFAULT; }
- @Many
@Documented @Retention(RetentionPolicy.RUNTIME) @Target({}) public @interface Many { String select() default ""; FetchType fetchType() default FetchType.DEFAULT; }
- @ResultMap
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface ResultMap { String[] value(); }
类型别名#
原来在xml版本配置mapper时,会使用parameterType
属性指定程序员提供的类的全类名,但是这个全类名真的太长了,于是MyBatis官方提供了给全类名取别名的标签,在Mybatis的主配置文件中添加如下的配置,如下:
<typeAliases> <typeAlias alias="user" type="com.changwu.pojo.User"/> </typeAliases>
添加了全类名的配置之后,我们的在mapper中就可以使用别名了,如下: 并且在windows系统下不区分大小写
<update id="updateUser" parameterType="UsEr"> update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} where id = #{id}" </update>
但是像上面这样,为每一个POJO都添加上别名的配置,确实显得有点麻烦,于是可以像下面这样,为一整个包下面的类名配置别名, 别名就是类名不区分大小写的格式
<typeAliases> <package alias="user" type="com.changwu.pojo"/> </typeAliases>
公共sql的抽取#
一处抽取,多处使用
<sql id="findUsers"> select * form user; </sql> <select id="findAll" resultType="com.changwu.pojo.User"> <include refid="findUsers"></include> </select>
优先级#
MyBatis支持的3种配置方式, 编码>properties配置文件>property元素标签, 优先级如下:
- 在properties配置文件中指定的配置文件的属性首先被读取
- 根据配置文件中的resources属性读取类路径下的属性文件,或者根据url属性读取属性文件并会覆盖同名属性
- 读取作为方法参数传递的属性,并覆盖已经读取的同名属性
不要混合使用xml版和注解版两种开发模式,否则Mybatis启动不了