Mybatis
什么是Mybatis?
MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAO)。
官网对Mybatis的介绍更加具有权威性:
MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架。MyBatis 避免了几乎所有的 JDBC 代码和手工设置参数以及抽取结果集。MyBatis 使用简单的 XML 或注解来配置和映射基本体,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。
MyBatis是iBatis的升级版,用法有很多的相似之处,但是MyBatis进行了重要的改进。例如:
1、Mybatis实现了接口绑定,使用更加方便。
在ibatis2.x中我们需要在DAO的实现类中指定具体对应哪个xml映射文件, 而Mybatis实现了DAO接口与xml映射文件的绑定,自动为我们生成接口的具体实现,使用起来变得更加省事和方便。
2、对象关系映射的改进,效率更高
3、MyBatis采用功能强大的基于OGNL的表达式来消除其他元素。
MyBatis的框架架构
看到Mybatis的框架图,可以清晰的看到Mybatis的整体核心对象
mybatis框架图MyBatis应用程序根据XML配置文件创建SqlSessionFactory,SqlSessionFactory在根据配置,配置来源于两个地方,一处是配置文件,一处是Java代码的注解,获取一个SqlSession。SqlSession包含了执行sql所需要的所有方法,可以通过SqlSession实例直接运行映射的sql语句,完成对数据的增删改查和事务提交等,用完之后关闭SqlSession。
MyBatis的优缺点
优点:
1、简单易学
mybatis本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。
2、灵活
mybatis不会对应用程序或者数据库的现有设计强加任何影响。 sql写在xml里,便于统一管理和优化。通过sql基本上可以实现我们不使用数据访问框架可以实现的所有功能,或许更多。
3、解除sql与程序代码的耦合
通过提供DAL层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。
4、提供映射标签,支持对象与数据库的orm字段关系映射
5、提供对象关系映射标签,支持对象关系组建维护
6、提供xml标签,支持编写动态sql。
缺点:
1、编写SQL语句时工作量很大,尤其是字段多、关联表多时,更是如此。
2、SQL语句依赖于数据库,导致数据库移植性差,不能更换数据库。
3、框架还是比较简陋,功能尚有缺失,虽然简化了数据绑定代码,但是整个底层数据库查询实际还是要自己写的,工作量也比较大,而且不太容易适应快速数据库修改。
4、二级缓存机制不佳
总结
mybatis的优点同样是mybatis的缺点,正因为mybatis使用简单,数据的可靠性、完整性的瓶颈便更多依赖于程序员对sql的使用水平上了。sql写在xml里,虽然方便了修改、优化和统一浏览,但可读性很低,调试也非常困难,也非常受限。
mybatis没有hibernate那么强大,但是mybatis最大的优点就是简单小巧易于上手,方便浏览修改sql语句。
实践:
项目是在springMVC 测试项目的基础之上进行的,相当于整合Spring-springMVC-Mybatis的一个项目。
首先在之前项目的基础上添加jar包,aopalliance.jar、aspjectj.jar、aspectjweaver.jar、c3po.jar、common-pool.jar、cglib.jar、javassist.jar、dom4j.jar、mybatis.jar、mybatis-spring.jar、mysql-connector-java-5.1.38-bin.jar、log4j.jar、log4j.core.jar、log4j-api.jar。
工程项目架构图web.xml中添加了对spring配置文件的加载
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:com/hb/test/resources/spring/applicationContext.xml
</param-value>
</context-param>
User类没变,不再展示。
创建User类对应的dao接口:
public interface UserMapper {
void save(User user);
boolean update(User user);
boolean delete(int id);
User findById(int id);
List<User> findAll();
}
User类的sql语句文件UserMapper.xml
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--
namespace:必须与对应的接口全类名一致
id:必须与对应接口的某个对应的方法名一致
-->
<mapper namespace="com.hb.test.mapper.UserMapper">
<insert id="save" parameterType="User">
insert into t_user(user_name,user_password,user_age) values(#{name},#{password},#{age})
</insert>
<update id="update" parameterType="User">
update t_user set user_name = #{name}, user_password = #{password}, user_age where user_id = #{id}
</update>
<delete id="delete" parameterType="int">
delete from t_user where user_id = #{id}
</delete>
<select id="findById" parameterType="int" resultType="User">
select * from t_user where user_id = #{id}
</select>
<select id="findAll" resultType="User">
select * from t_user
</select>
</mapper>
创建MyBatis的mapper配置文件
在src/com/hb/test/resources/mabatis中创建MyBatis配置文件:mybatis-config.xml。
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 实体类,简称 -设置别名 -->
<typeAliases>
<typeAlias alias="User" type="com.hb.test.bean.User"/>
</typeAliases>
<!-- 实体接口映射资源 -->
<!--
说明:如果xxMapper.xml配置文件放在和xxMapper.java统一目录下,mappers也可以省略,因为org.mybatis.spring.mapper.MapperFactoryBean默认会去查找与xxMapper.java相同目录和名称的xxMapper.xml
-->
<mappers>
<mapper resource="com/hb/test/mapper/UserMapper.xml"/>
</mappers>
</configuration>
spring配置文件:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.1.xsd">
<context:annotation-config/>
<context:component-scan base-package="com.hb.test.service"></context:component-scan>
<!-- spring加载外部配置文件 -->
<util:properties id="dataSourceProps" location="classpath:com/hb/test/resources/resource/jdbc.properties"/>
<!-- 定义数据源Bean,使用C3P0数据源实现 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="#{dataSourceProps['jdbc.driverClass']}"/>
<property name="jdbcUrl" value="#{dataSourceProps['jdbc.jdbcUrl']}"/>
<property name="user" value="#{dataSourceProps['jdbc.user']}"/>
<property name="password" value="#{dataSourceProps['jdbc.password']}"/>
<property name="maxPoolSize" value="#{dataSourceProps['jdbc.maxPoolSize']}"/>
<property name="minPoolSize" value="#{dataSourceProps['jdbc.minPoolSize']}"/>
<property name="initialPoolSize" value="#{dataSourceProps['jdbc.initialPoolSize']}"/>
<property name="maxIdleTime" value="#{dataSourceProps['jdbc.maxIdleTime']}"/>
</bean>
<!--
2. mybatis的SqlSession的工厂: SqlSessionFactoryBean dataSource:引用数据源
MyBatis定义数据源,同意加载配置
-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:com/hb/test/resources/mybatis/mybatis-config.xml" />
</bean>
<!--
3. mybatis自动扫描加载Sql映射文件/接口 : MapperScannerConfigurer sqlSessionFactory
basePackage:指定sql映射文件/接口所在的包(自动扫描)
-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.hb.test.mapper"/>
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
<!-- 4. 事务管理 : DataSourceTransactionManager dataSource:引用上面定义的数据源
-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 5. 使用声明式事务
transaction-manager:引用上面定义的事务管理器
-->
<tx:annotation-driven transaction-manager="txManager"/>
</beans>
User业务类
@Service
public class UserServiceImpl implements UserService{
@Autowired
private UserMapper userMapper;
@Override
public void save(User user) {
userMapper.save(user);
}
@Override
public boolean update(User user) {
return userMapper.update(user);
}
@Override
public boolean delete(int id) {
return userMapper.delete(id);
}
@Override
public User findById(int id) {
return userMapper.findById(id);
}
@Override
public List<User> findAll() {
return userMapper.findAll();
}
}
User类控制器
@Controller
public class UserController {
@Resource(name="userServiceImpl")
private UserService us;
@RequestMapping("")
public String create(Model model){
return "create";
}
@RequestMapping("/save")
public String Save(@ModelAttribute("form") User user, Model model){
us.save(user);
model.addAttribute("user", user);
return "detail";
}
}
运行结果
在实际操作过程中又遇到了一个坑,在容器启动时报如下错误,让我折腾了好久。
Failed to convert property value of type 'java.lang.String' to required type 'int' for property 'maxPoolSize'; nested exception is java.lang.NumberFormatException: For input string: "${jdbc.maxPoolSize}"
报错时使用的加载外部资源代码如下:
<bean id="mappings" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="classpath:com/hb/test/resources/resource/jdbc.properties"></property>
</bean>
原因是当我加入了MapperScannerConfigurer他会优先于PropertyPlaceholderConfigurer执行,所以这个时候,${jdbc.maxPoolSize}还没有被解析呢,故没有被mysql.properties里面的值所替换,所以出现NumberFormatException就是情理之中了这是mybatis-spring的一个己经公开的问题.
解决方法是使用spring的<util:properties id="dataSourceProps"> 加载指定的jdbc.properties,也可以使用其他的方式,我没有试过.