3. Mybatis的Dao开发
使用Mybatis开发Dao,通常有两个方法,即原始Dao开发方法和Mapper接口开发方法。
3.1 Mybatis的核心对象
-
SqlSessionFactoryBuilder
SqlSessionFactoryBuilder用于创建SqlSessionFacoty,SqlSessionFacoty一旦创建完成就不需要SqlSessionFactoryBuilder了,因为SqlSession是通过SqlSessionFactory生产,所以可以将SqlSessionFactoryBuilder当成一个工具类使用,最佳使用范围是静态代码块中的局部变量
-
SqlSessionFactory
SqlSessionFactory是一个接口,接口中定义了openSession的不同重载方法,SqlSessionFactory的最佳使用范围是整个应用运行期间,一旦创建后可以重复使用,通常以单例模式管理SqlSessionFactory。
-
SqlSession
SqlSession是一个面向用户的接口, sqlSession中定义了数据库操作,默认使用DefaultSqlSession实现类。
SqlSession的实现类即DefaultSqlSession,此对象中对操作数据库实质上用的是Executor
每个线程都应该有它自己的SqlSession实例。SqlSession的实例不能共享使用,它也是线程不安全的。因此最佳的范围是请求或方法范围。绝对不能将SqlSession实例的引用放在一个类的静态字段或实例字段中。
4. 打开一个 SqlSession;使用完毕就要关闭它。通常把这个关闭操作放到 finally 块中以确保每次都能执行关闭。
3.2 传统的DAO开发模式
程序员需要写dao接口和dao实现类。
需要向dao实现类中注入SqlSessionFactory,在方法体内通过SqlSessionFactory创建SqlSession
步骤:
- 编写到接口以及实现类
import java.util.List; public interface IUserDao { /** * 根据ID查询User * @param id 用户Id * @return 用户信息 * @throws Exception */ public User findUserById(int id) throws Exception; /** * 根据用户的名字模糊查询 * @param name 用户的username * @return 一组用户 * @throws Exception */ public List<User> findUserByName(String name) throws Exception; /** * 添加用户 * @param user 用户信息 * @return 包含主键的用户 * @throws Exception */ public User addUser(User user) throws Exception; }
public class UserDaoImpl implements IUserDao { //需要往UserDaoImpl注入SQLSessionFactory private SqlSessionFactory sqlSessionFactory; //通过构造方法注入 public UserDaoImpl(SqlSessionFactory sqlSessionFactory) { this.sqlSessionFactory = sqlSessionFactory; } @Override public User findUserById(int id) throws Exception { /** * 1.得到SqlSession * 2.调用SqlSession对应的方法(selectOne)来操作数据库 * 3.关闭SqlSession * 4.返回结果 */ SqlSession sqlSession = sqlSessionFactory.openSession(); User user = sqlSession.selectOne("test.findUserById", id); sqlSession.close(); return user; } @Override public List<User> findUserByName(String name) throws Exception { /** * 1.得到SqlSession * 2.调用SqlSession对应的方法(selectList)来操作数据库 * 3.关闭SqlSession * 4.返回结果 */ SqlSession sqlSession = sqlSessionFactory.openSession(); List<User> users = sqlSession.selectList("test.findUserByName", "%"+name+"%"); sqlSession.close(); return users; } @Override public void addUser(User user) throws Exception { /** * 1.得到SqlSession * 2.调用SqlSession对应的方法(insert)来操作数据库 * 3.提交事务 * 4.关闭SqlSession */ SqlSession sqlSession = sqlSessionFactory.openSession(); sqlSession.insert("test.addUser", user); sqlSession.commit(); sqlSession.close(); } }
编写SQL映射文件
<!-- 根据ID查询 --> <select id="findUserById" parameterType="int" resultType="org.csmf.mybatis.entity.User"> select * from t_user where id = #{id} </select> <!-- 根据姓名模糊查询 --> <select id="findUserByName" parameterType="string" resultType="org.csmf.mybatis.entity.User"> select * from t_user where username like #{name} </select> <!—添加用户--> <insert id="addUser" parameterType="org.csmf.mybatis.entity.User"> <selectKey order="AFTER" resultType="int" keyProperty="id"> select last_insert_id() </selectKey> insert into t_user(username,password,sex,brithday,address) values(#{username},#{password},#{sex},#{brithday},#{address}) </insert>
总结:
原始Dao开发中存在以下问题:
Dao方法体存在重复代码:通过SqlSessionFactory创建SqlSession,调用SqlSession的数据库操作方法
调用sqlSession的数据库操作方法需要指定statement的id,这里存在硬编码,不得于开发维护。
3.3 Mapper代理模式
Mapper接口开发方法只需要程序员编写Mapper接口(相当于Dao接口),由Mybatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法。
Mapper接口开发需要遵循以下规范:
- 在mapper.xml中namespace写的是对应Mapper接口的全限定名
Mapper接口方法名和Mapper.xml中定义的每个statement的id相同
Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同
实现:
UserMapper.xml:
UserMapper接口:
测试代码:
import java.io.IOException;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.csmf.mybatis.entity.User;
import org.junit.Before;
import org.junit.Test;
/**
* 测试UserMapper的测试类
* @author pactera
*
*/
public class UserMapperTest {
private SqlSessionFactory sqlSessionFactory;
@Before
public void setUp(){
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream("mybatis-config.xml");
} catch (IOException e) {
e.printStackTrace();
}
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void testFindUserById() throws Exception{
SqlSession sqlSession = sqlSessionFactory.openSession();
//创建UserMapper的休息,Mybatis可以自动生成UserMapper接口的代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.findUserById(2);
System.out.println(user);
}
}
总结:
- selectOne和selectList
动态代理对象调用sqlSession.selectOne()和sqlSession.selectList()是根据mapper接口方法的返回值决定,如果返回list则调用selectList方法,如果返回单个对象则调用selectOne方法。
- namespace
mybatis官方推荐使用mapper代理方法开发mapper接口,程序员不用编写mapper接口实现类,使用mapper代理方法时,输入参数可以使用包装类或map对象,保证dao的通用性。
4. mybatis-config.xml文件的详情
mybatis的全局配置文件SqlMapConfig.xml,配置内容如下:
properties(属性) 加载properties文件
settings(全局配置参数) 缓存
typeAliases(类型别名) 类型别名
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件) 通用Mapper 分页插件
environments(环境集合属性对象)
environment(环境子属性对象)
transactionManager(事务管理)
dataSource(数据源)
mappers(映射器)
4.1 properties属性
在实际开发中,我们通常会将数据库连接参数单独配置在db.properties中,只需要在mybatis-config.xml中加载db.properties的属性值。这样我们在mybatis-config.xml中就不需要对数据库连接参数硬编码。
将数据库连接参数只配置在db.properties中,原因:方便对参数进行统一管理,其它xml可以引用该db.properties。
db.properties文件:
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8
jdbc.username=root
jdbc.password=root
mybatis-config.xml引用properties文件:
<!-- 加载properties文件 -->
<properties resource="db.properties"></properties>
<!-- 和spring整合后 environments配置将废除-->
<environments default="development">
<!-- 可以配置多个environment -->
<environment id="development">
<!-- 使用jdbc事务管理-->
<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>
4.2 settings全局参数配置
mybatis框架在运行时可以调整一些运行参数。
比如:开启二级缓存、开启延迟加载。
全局参数将会影响mybatis的运行行为。
配置选项 | 描述 | 可选值 | 默认值 |
---|---|---|---|
cacheEnabled | 全局启用或禁用任何Mapper中配置的缓存 | true|false | true |
lazyLoadingEnabled | 全局启用或禁用延迟加载。当启用后,所有的关系都将被延迟加载。可以通过使用特定关系上的fetchType属性来取代该值。 | true|false | false |
aggressiveLazyLoading | 当启用时,具有惰性加载属性的对象将完全在调用任何惰性属性时加载。否则,每个属性都将按需加载。 | true|false | true |
multipleResultSetsEnabled | 允许或不允许从单个语句返回多个结果集(需要兼容的驱动程序) | true|false | true |
useColumnLabe | 使用列标签,而不是列名称。不同的驱动程序在这方面的行为也有所不同。请参考驱动程序文档,或测试这两种模式,以确定驱动程序的行为方式。 | true|false | true |
useGeneratedKeys | 允许JDBC支持已生成的密钥。这时需要一个兼容的驱动程序。这个设置强制生成的键在设置为true时使用,因为一些驱动程序拒绝兼容性但仍然可以工作(例如Derby)。 | true|false | false |
autoMappingBehavior | 指定MyBatis是否以及如何自动将列映射到字段/属性。NONE禁用自动映射。PARTIAL只自动映射简单,非嵌套的结果集。FULL将自动映射任何复杂度的结果集(包含嵌套或非嵌套的)。 | NONE, PARTIAL, FULL | PARTIAL |
defaultExecutorType | 配置默认的执行器(Executor): SIMPLE: 简单执行器 REUSE:重用prepared statement的执行器 BATCH:重用statement并且执行批量更新的执行器 | SIMPLE REUSE BATCH | SIMPLE |
defaultStatementTimeout | 设置数据库超时时间(单位:秒) | 任何正数 | Not Set (null) |
safeRowBoundsEnabled | 允许在嵌套语句上使用行边界。 | true|false | false |
mapUnderscoreToCamelCase | 是否开启, 数据库列:A_COLUMN自动映射为属性:aColumn | true|false | false |
localCacheScope | MyBatis使用本地缓存来提高查询效率,本地缓存默认Scope是:SESSION, 在同一次SESSION期间执行的所有查询都将被缓存. localCacheScope=STATEME:本地会话将仅用于stateme的执行,对同一Sql会话的两个不同调用之间将不共享任何数据 | SESSION|STATEMENT | SESSION |
jdbcTypeForNull | 指定空值的JDBC类型,当没有为参数提供特定的JDBC类型时。一些驱动程序需要指定列JDBC类型,常用的值:NULL,VARCHAR,OTHER | JdbcType enumeration.Most common are: NULL,VARCHAR and OTHER | OTHER |
lazyLoadTriggerMethods | 指定哪个对象的方法会触发延迟加载 | 方法名列表,多个方法名之间使用逗号分割 | equlas, clone, hashCode, toString |
defaultScriptingLanguage | 指定动态SQL生成时默认使用的语言。 | 一个类型的别名或完全限定的类名。 | |
callSettersOnNulls | 指定在检索到的值为空时,是否会调用设置器或映射的put方法。当您依赖于Map.keySet()或空值初始化时,它很有用。请注意原语,如(int、布尔值等)。将不会被设置为null。 | true|false | false |
logPrefix | 指定MyBatis将添加到日志记录器名称中的前缀字符串。 | 任何字符串 | 未设置 |
logImpl | 指定MyBatis应该使用哪些日志记录实现类。如果此设置日志记录类不存在,则将自动发现日志记录实现。 | SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING | 为设置 |
proxyFactory | 代理工厂指定MyBatis将用于创建能够实现延迟加载功能的对象的代理工具。 | CGLIB|JAVASSIST | CGLIB |
4.3 typeAliases(类型别名)
4.3.1 mybatis支持别名:
别名 | 映射的类型 |
---|---|
_byte | byte |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | String |
byte | Byte |
long | Long |
short | Short |
int | Integer |
integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | Date |
decimal | BigDecimal |
bigdecimal | BigDecimal |
4.3.2 自定义别名:
在mybatis-config.xml中配置:
<typeAliases>
<!-- 单个别名定义 -->
<typeAlias alias="user" type="org.csmf.mybatis.entity.User"/>
<!-- 批量别名定义,扫描整个包下的类,别名为类名(首字母大写或小写都可以) -->
<package name=" org.csmf.mybatis.entity "/>
<package name="其它包"/>
</typeAliases>
使用别名:
4.4 mappers(映射器)
Mapper配置的几种方法:
<mapper resource=" " />
使用相对于类路径的资源
如:
<mapper resource="sqlmap/User.xml" />
<mapper url=" " />
使用完全限定路径
如:
<mapper url="file:///D:\workspace_spingmvc\mybatis_01\config\sqlmap\User.xml" />
<mapper class=" " />
使用mapper接口类路径
如:
<mapper class="cn.itcast.mybatis.mapper.UserMapper"/>
注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。
<package name=""/>
注册指定包下的所有mapper接口
如:
<package name="org.csmf.mybatis.dao"/>
注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。