各框架原理

Mybatis

2017-02-25  本文已影响134人  FTOLsXD

什么是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,也可以使用其他的方式,我没有试过.

上一篇下一篇

猜你喜欢

热点阅读