J2EE
开发界中一个永远避免不了的争论就是Hibernate
与Mybatis
哪个好。虽然我自己倾向Hibernate
,但这两种ORM
框架我也都没有在比较大型的场景上使用过,没有资格做评价。
最近公司有一个项目,主程让我用Spring
与Mybatis
搭建服务端框架,这里就做一下简单的学习记录。
Java Config方式的Mybatis启动配置
首先在maven
中加上Mybatis
与mybatis-spring
的依赖:
org.mybatis
mybatis-spring
1.2.2
org.mybatis
mybatis
3.2.8
前一篇中提到,公司推荐Spring
使用Java Config
的方式进行配置,所以Mybatis
也不例外。
先看一段网上找到比较常见的xml方式配置:
配置的核心是sqlSessionFactory
,将dataSource
数据源注入就完成了Mybatis
会话工厂的实例化了。
接着把这段配置翻译成Java config
版本:
@Configuration
@PropertySource("classpath:druid-config.properties")
public class JdbcConfig {
@Value("${druid.driverClassName}")
private String driverClassName;
@Value("${druid.url}")
private String url;
@Value("${druid.user}")
private String username;
@Value("${druid.password}")
private String password;
@Value("${druid.maxActive}")
private int maxActive;
@Bean
public DataSource dataSource(){
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName(driverClassName);
ds.setUrl(url);
ds.setUsername(username);
ds.setPassword(password);
ds.setMaxActive(maxActive);
ds.setMinIdle(0);
return ds;
}
@Bean
public SqlSessionFactoryBean sqlSessionFactory() throws IOException {
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
//加载匹配到的xml映射配置
Resource[] resources = resolver.getResources("classpath*:com/nd/mathlearning/server/*/dao/mapper/*.xml");
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource());
bean.setMapperLocations(resources);
//加载Mybatis配置
bean.setConfigLocation(resolver.getResource("classpath:mybatis/mybatis-config.xml"));
return bean;
}
@Bean
public SqlSessionTemplate sqlSessionTemplate() throws Exception {
SqlSessionTemplate bean = new SqlSessionTemplate(sqlSessionFactory().getObject());
return bean;
}
}
-
@Configuration
表明此类用作Spring配置
-
@PropertySource
说明读取哪个配置文件 -
@Value
将properties
中的配置项注入进变量 -
@Bean
注解的方法表明实例化对象。 -
ResourcePatternResolver
是用来加载工程配置文件。
Mybatis的xml映射文件
与Hibernate
类似,Mybatis
需要写xml
配置来实现Java
类与数据库表之间的映射关系,更多的,Mybatis
框架执行的所有sql
也都配置在xml
中。
来看一段完整的xml配置:
uc_user_id, user_id, user_name
delete from uc_userid_map
where uc_user_id = #{ucUserId,jdbcType=BIGINT}
insert into uc_userid_map (uc_user_id, user_id, user_name
)
values (#{ucUserId,jdbcType=BIGINT}, #{userId,jdbcType=BIGINT}, #{userName,jdbcType=VARCHAR}
)
update uc_userid_map
user_id = #{userId,jdbcType=BIGINT},
user_name = #{userName,jdbcType=VARCHAR},
where uc_user_id = #{ucUserId,jdbcType=BIGINT}
- 根节点
mapper
中的namespace
声明了此配置的唯一编号,在此后的java
的api
调用就需要用到此属性。 -
resultMap
定义表到java
实体类之间的字段映射关系 -
column
是表字段 -
property
是类属性 -
jdbcType
是表字段类型。 -
sql
节点在这里定义了一个sql
查询片段,使用include
语法拼接,用于简化配置。 -
select
,insert
,update
,delete
节点定义四种sql
操作的方法。id
定义方法的标识; -
parameterType
说明方法的参数类型,可以是javabean
,但一个方法只能有一个输入参数。
更详细的mapper
配置说明参考官网:
- Mapper XML 文件
mapper与实体生成工具
既然Mybatis
所有的逻辑操作都是配置在xml
中,那就把原来在java
中的sql
开发工作转换到了xml
上。
与Hibernate
一样,Mybatis
提供了一个简易的逆向工程工具,帮助我们根据已有数据表生成对应的javabean
和基本操作xml
配置。
在maven
中增加生成器依赖:
org.mybatis.generator
mybatis-generator-core
1.3.2
编写生成配置xml
有了生成配置xml
后就可以调用api
生成实体javabean
,dao
和mapper
:
public class MyBatisGenerator {
public static void main(String args[]){
String config ="";
try {
config = MyBatisGenerator.class.getResource("generator.xml").toURI().getPath();
} catch (URISyntaxException e) {
e.printStackTrace();
}
String[] arg = { "-configfile", config, "-overwrite" };
ShellRunner.main(arg);
}
}
参考文章:
- 使用Mybatis-Generator自动生成Dao、Model、Mapping相关文件
与Spring整合
在j2ee
的各种开源框架中,我们最关心的可能就是怎么与Spring
做整合。
数据映射器 MapperFactoryBean
Mybatis
提供了一种简易的整合方式,使用class
的方式定义mapper
,而不需要配置xml
。
例如定义一个interface
的mapper
:
public interface UserMapper {
@Select("SELECT * FROM user WHERE id = #{userId}")
User getUser(@Param("userId") long id);
}
然后在spring
中定义:
Spring
容器中就产生了userMapper
实例可以来执行我们想要的数据操作。这种方式有点类似spring data
中CrudRepository
的@Query
用法。
抽象类SqlSessionDaoSupport的整合方式
从前面的启动配置我们知道了,Mybatis
的api
入口是sqlSessionFactory
。Mybatis
提供SqlSessionDaoSupport
抽象类来方便获取sqlSession
。
先看一个UserDao的实现:
public class UserDao extends SqlSessionDaoSupport{
@Autowired
public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
super.setSqlSessionTemplate(sqlSessionTemplate);
}
public User getUserById(User user) {
return (User) getSqlSession().selectOne("com.xxt.ibatis.dbcp.domain.User.getUser", user);
}
}
继承SqlSessionDaoSupport
可以获得getSqlSession
方法来得到sqlSession
。然后通过sqlSession
的api
最终调用到mapper
里配置的sql
程序。
sqlSession几个常用的api:
T selectOne(String statement, Object parameter)
List selectList(String statement, Object parameter)
Map selectMap(String statement, Object parameter, String mapKey)
int insert(String statement, Object parameter)
int update(String statement, Object parameter)
int delete(String statement, Object parameter)
第一个参数表示mapper中方法的唯一标识,结构为:mapper标识 + . + 方法标识。
mybatis-spring 1.2版本中SqlSessionDaoSupport的变化
首先看1.2版本中SqlSessionDaoSupport
的注释信息:
/**
* Convenient super class for MyBatis SqlSession data access objects.
* It gives you access to the template which can then be used to execute SQL methods.
*
* This class needs a SqlSessionTemplate or a SqlSessionFactory.
* If both are set the SqlSessionFactory will be ignored.
*
* {code Autowired} was removed from setSqlSessionTemplate and setSqlSessionFactory
* in version 1.2.0.
*
* @author Putthibong Boonbong
*
* @see #setSqlSessionFactory
* @see #setSqlSessionTemplate
* @see SqlSessionTemplate
* @version $Id$
*/
之前版本中setSqlSessionTemplate
和setSqlSessionFactory
是Autowired
,1.2之后需要我们手动注入。
这个改动应该是为了支持一个项目中建立多数据源的场景,我们可以用SqlSessionDaoSupport
创建多个DAO层基类,选择不同的数据源注入。
参考文章:
- spring与mybatis三种整合方法
- Java API sqlSeesion
Mybatis插件配置
Mybatis
提供了插件机制实现了功能扩展
分页插件PageHelper
数据库列表查询少不了分页功能,怎么在代码级别上简单有效地处理分页逻辑总是一大挑战。
实现分页功能关键是根据 两个参数 得到 两个数据:
- 两个参数:数据偏移量 和 页面大小(或者通过 当前页码 和 页面大小 进行转换)
- 两个数据:当前页面的列表数据 和 总记录数
在sql
层面上讲,需要两次查询:
- 根据查询条件用
limit
参数(在oracle
用rownum
)得到当前请求页面数据。 - 用相同的条件
count(*)
查询数据库中存在的总数。
回到Mybatis
,我们可以利用它的插件机制实现分页功能。
首先加上PageHelper
的maven
配置:
com.github.pagehelper
pagehelper
3.7.1
在初始化sqlSessionFactory
时加上插件配置:
@Bean
public SqlSessionFactoryBean sqlSessionFactory() throws IOException {
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources("classpath*:com/nd/mathlearning/server/*/dao/mapper/*.xml");
Interceptor[] plugings = new Interceptor[1];
PageHelper pageHelper = new PageHelper();//mybatis分页插件
Properties p = new Properties();//插件属性配置
p.put("dialect", "mysql"); //数据库言
p.put("reasonable", "true");//参数合理化,如果pageNum<1会查询第一页,如果pageNum>pages会查询最后一页
p.put("offsetAsPageNum", "trsue"); //将RowBounds第一个参数offset当成pageNum页码使用
pageHelper.setProperties(p);
plugings[0] = pageHelper;
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource());
bean.setMapperLocations(resources);
bean.setPlugins(plugings); //插件注入到sqlSessionFactory
bean.setConfigLocation(resolver.getResource("classpath:mybatis/mybatis-config.xml"));
return bean;
}
其中属性配置详细信息参考项目主页。
在调用Mybatis
的api
时增加RowBounds
参数实现分页:
List list = sqlSession.selectList("x.y.selectIf", null, new RowBounds(1, 10));
项目主页:
- Mybatis-PageHelper