J2EE
开发界中一个永远避免不了的争论就是Hibernate
与Mybatis
哪个好。虽然我自己倾向Hibernate
,但这两种ORM
框架我也都没有在比较大型的场景上使用过,没有资格做评价。
最近公司有一个项目,主程让我用Spring
与Mybatis
搭建服务端框架,这里就做一下简单的学习记录。
Java Config方式的Mybatis启动配置
首先在maven
中加上Mybatis
与mybatis-spring
的依赖:
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.8</version>
</dependency>
前一篇中提到,公司推荐Spring
使用Java Config
的方式进行配置,所以Mybatis
也不例外。
先看一段网上找到比较常见的xml方式配置:
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--指定实体类映射文件,可以指定同时指定某一包以及子包下面的所有配置文件 -->
<property name="mapperLocations" value="classpath*:com/nd/mathlearning/server/*/dao/mapper/*.xml"/>
<!--mybatis全局配置文件 -->
<property name="configLocation" value="classpath:mybatis/mybatis-config.xml" />
<property name="dataSource" ref="dataSource" />
</bean>
配置的核心是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配置:
<?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.nd.mathlearning.server.personal.dao.UcUseridMapMapper" >
<resultMap id="BaseResultMap" type="com.nd.mathlearning.server.personal.dto.UcUseridMapEntity" >
<id column="uc_user_id" property="ucUserId" jdbcType="BIGINT" />
<result column="user_id" property="userId" jdbcType="BIGINT" />
<result column="user_name" property="userName" jdbcType="VARCHAR" />
</resultMap>
<sql id="Base_Column_List" >
uc_user_id, user_id, user_name
</sql>
<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Long" >
select
<include refid="Base_Column_List" />
from uc_userid_map
where uc_user_id = #{ucUserId,jdbcType=BIGINT}
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.Long" >
delete from uc_userid_map
where uc_user_id = #{ucUserId,jdbcType=BIGINT}
</delete>
<insert id="insert" parameterType="com.nd.mathlearning.server.personal.dto.UcUseridMapEntity" >
insert into uc_userid_map (uc_user_id, user_id, user_name
)
values (#{ucUserId,jdbcType=BIGINT}, #{userId,jdbcType=BIGINT}, #{userName,jdbcType=VARCHAR}
)
</insert>
<update id="updateByPrimaryKeySelective" parameterType="com.nd.mathlearning.server.personal.dto.UcUseridMapEntity" >
update uc_userid_map
<set >
<if test="userId != null" >
user_id = #{userId,jdbcType=BIGINT},
</if>
<if test="userName != null" >
user_name = #{userName,jdbcType=VARCHAR},
</if>
</set>
where uc_user_id = #{ucUserId,jdbcType=BIGINT}
</update>
</mapper>
- 根节点
mapper
中的namespace
声明了此配置的唯一编号,在此后的java
的api
调用就需要用到此属性。 -
resultMap
定义表到java
实体类之间的字段映射关系 -
column
是表字段 -
property
是类属性 -
jdbcType
是表字段类型。 -
sql
节点在这里定义了一个sql
查询片段,使用include
语法拼接,用于简化配置。 -
select
,insert
,update
,delete
节点定义四种sql
操作的方法。id
定义方法的标识; -
parameterType
说明方法的参数类型,可以是javabean
,但一个方法只能有一个输入参数。
更详细的mapper
配置说明参考官网:
mapper与实体生成工具
既然Mybatis
所有的逻辑操作都是配置在xml
中,那就把原来在java
中的sql
开发工作转换到了xml
上。
与Hibernate
一样,Mybatis
提供了一个简易的逆向工程工具,帮助我们根据已有数据表生成对应的javabean
和基本操作xml
配置。
在maven
中增加生成器依赖:
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.2</version>
</dependency>
编写生成配置xml
<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd" >
<generatorConfiguration>
<!-- classPathEntry:数据库的JDBC驱动 -->
<classPathEntry location="F:\mylib\mysql-connector-java-5.1.30.jar" />
<!--生成映射的类型,也可以生成ibatis的。具体参看mybatis-generator -->
<context id="DB2Tables3" targetRuntime="MyBatis3">
<plugin type="org.mybatis.generator.plugins.CaseInsensitiveLikePlugin"></plugin>
<plugin type="org.mybatis.generator.plugins.SerializablePlugin"></plugin>
<!-- 去除自动生成的注释 -->
<commentGenerator>
<property name="suppressDate" value="true" />
<property name="suppressAllComments" value="true" />
</commentGenerator>
<!-- 数据库配置 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://172.24.133.201:3306/mylearning" userId="user_account"
password="user_account">
</jdbcConnection>
<javaTypeResolver>
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!--以下三个标签主要解析targetPackage和targetProject。其它的具体参看mybatis-generator -->
<!-- targetProject:自动生成代码的位置 -->
<javaModelGenerator targetPackage="com.nd.mathlearning.server.microclass.dto" targetProject="src/main/java/">
<property name="enableSubPackages" value="true" />
<property name="trimStrings" value="true" />
</javaModelGenerator>
<sqlMapGenerator targetPackage="com.nd.mathlearning.server.microclass.dao" targetProject="src/main/java/">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<!-- 要生成的表配置 -->
<table tableName="mc_note" domainObjectName="McNoteEntity" enableCountByExample="false" enableUpdateByExample="false"
enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"></table>
</context>
</generatorConfiguration>
有了生成配置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);
}
}
参考文章:
与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
中定义:
<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="com.xxt.ibatis.dbcp.dao.UserMapper" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
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> T selectOne(String statement, Object parameter)
<E> List<E> selectList(String statement, Object parameter)
<K,V> Map<K,V> 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.
* <p>
* This class needs a SqlSessionTemplate or a SqlSessionFactory.
* If both are set the SqlSessionFactory will be ignored.
* <p>
* {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层基类,选择不同的数据源注入。
参考文章:
Mybatis插件配置
Mybatis
提供了插件机制实现了功能扩展
分页插件PageHelper
数据库列表查询少不了分页功能,怎么在代码级别上简单有效地处理分页逻辑总是一大挑战。
实现分页功能关键是根据 两个参数 得到 两个数据:
- 两个参数:数据偏移量 和 页面大小(或者通过 当前页码 和 页面大小 进行转换)
- 两个数据:当前页面的列表数据 和 总记录数
在sql
层面上讲,需要两次查询:
- 根据查询条件用
limit
参数(在oracle
用rownum
)得到当前请求页面数据。 - 用相同的条件
count(*)
查询数据库中存在的总数。
回到Mybatis
,我们可以利用它的插件机制实现分页功能。
首先加上PageHelper
的maven
配置:
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>3.7.1</version>
</dependency>
在初始化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<Country> list = sqlSession.selectList("x.y.selectIf", null, new RowBounds(1, 10));
项目主页: