MyBatis框架浅析之 初识MyBatis

更详细的内容可以查看 官方给出的文档 : http://www.mybatis.org/mybatis-3/zh/getting-started.html

 

一.重要的SqlSessionFactory对象

 

见名知意, SqlSessionFactory是生产 SqlSession的工厂, 而执行SQL语句必须要通过 SqlSession对象 ,因此获取SqlSessionFactory 是第一步

根据SqlSessionFactoryBuilder类中的 实例方法 build() 一般使用下图中的两种方式重载方法构建SqlSessionFactory对象, 1种是通过接收InputStream 刘对象
另一种是通过接收Configuration对象. 通常更多的是创建配置文件,使用第一种方式,即传入InputStream对象

 

获取SqlSessionFactory对象的方式共两种:
/**** 方式一: 从 XML 中构建 SqlSessionFactory ****/
String resource = "mybatis_config.xml";
// 加载mybatis的配置文件(它也加载关联的映射文件)
InputStream inputStream = null;
try {
    inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
    e.printStackTrace();
}
// 构建sqlSession的工厂
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

附上项目结构:

附上供于本地调试的POM文件:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>mybatis_study</groupId>
    <artifactId>mybatis_study</artifactId>
    <version>1.0-SNAPSHOT</version>


    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.1</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/junit/junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.11</version>
        </dependency>

    </dependencies>
</project>

附上 mybatis的 XML配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
    <!--为类的权限定名称设置简短的别名-->
    <typeAliases>
        <typeAlias type="com.baoxian.domain.UserInfo" alias="UserInfo"/>
    </typeAliases>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/> <!--事务管理类型-->
            <dataSource type="POOLED">
                <property name="username" value="root"/>
                <property name="password" value=""/>
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/crm?serverTimezone=GMT%2B8"/>
            </dataSource>
        </environment>
    </environments>
    
    <!--存放处理SQL语句的XML文件-->
    <mappers>
        <mapper resource="com/baoxian/dao/UserInfoMapper.xml"/>
        <mapper resource="com/baoxian/dao/ProductMapper.xml"/>
    </mappers>
</configuration>
/**** 方式二: 不使用 XML 构建 SqlSessionFactory ****/
DataSource dataSource = BlogDataSourceFactory.getBlogDataSource();
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
configuration.addMapper(BlogMapper.class);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);

//此种方式没有写过测试, 详细细节待补充;对此种构建方式有兴趣的小伙伴可以自行查阅资料(但一般都是使用第一种方式)

 

 

二.通过SqlSeesionFactory 创建 SqlSession对象

 

//创建SQLSession
/*
            sqlSession默认是不自动提交事务的,所以insert,delete ,update 默认是不会生效的
            开启自动提交事务的两种解决方案:
            1.openSession(true);
            2.sqlSession.commit();手动提交事务
*/
SqlSession sqlSession = sessionFactory.openSession(true);

 

三 .通过SqlSession对象执行一条简单的select 语句:

执行select语句的方式同样有两种:

方式一:直接通过SqlSession对象中封装的实例方法,执行SQL语句

    @Test
    public void testSelectOne() {
        try{
            UserInfo user = sqlSession.selectOne("com.baoxian.dao.UserInfoMapper.selectUserById", 1L);
            System.out.println(user);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            sqlSession.close();
        }

    }
附上 UserInfo 的实例类:
package com.baoxian.domain;

public class UserInfo {

    private long id;
    private String userName;
    private int age;


    public long getId() {
        return id;
    }

    public String getUserName() {
        return userName;
    }

    public int getAge() {
        return age;
    }

    public void setId(long id) {
        this.id = id;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "UserInfo{" +
                "id=" + id +
                ", name='" + userName + '\'' +
                ", age=" + age +
                '}';
    }
}

附上userInfo的表设计:



方式二:通过SqlSession获取 Mapper接口对象,再通过接口对象调用接口中的方法(接口中的方法名称,返回值,形参要与Mapper.xml文件中保持一致)

相比方式一: 此种方式更加直观,而且不用传入较长的xml文件路径 ,避免了代码的写死 ,因此推荐方式二
  @Test
    public void testSelectOne2() {
        try{
            UserInfoMapper mapper = sqlSession.getMapper(UserInfoMapper.class);
            UserInfo userInfo = mapper.selectUserById(1L);
            System.out.println(userInfo);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            sqlSession.close();
        }
    }

 

 附上UserInfoMapper接口的代码:

package com.baoxian.dao;

import com.baoxian.domain.UserInfo;

import java.util.List;

public interface UserInfoMapper {

    //查询全部User
    public List<UserInfo> selectAll();

    //根据user的ID查询User对象
    public UserInfo selectUserById(Long id);
}

附上UserInfoMapper.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.baoxian.dao.UserInfoMapper" >
  <!--
    正常情况下 type 需要写上实体类的全限定名称,但是由于在 mybatis的配置文件中,即 mybatis_config.xml中指定了别名,所以可以使用别名的方式
    <resultMap id="BaseResultMap" type="com.baoxian.domain.UserInfo" >
    -->
  <resultMap id="BaseResultMap" type="UserInfo" >
    <id column="id" property="id"  />
    <result column="username" property="userName"  />
    <result column="age" property="age"  />
  </resultMap>

  <!--通过ID删除用户-->
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Long" >
    delete from UserInfo
    where id = #{id}
  </delete>

  <!--新增用户-->
  <insert id="insert" parameterType="com.baoxian.domain.UserInfo" useGeneratedKeys="true" keyProperty="id" >
    insert into UserInfo (username,age)
    values (#{userName},#{age})
  </insert>

  <!--通过ID更新用户-->
  <update id="updateByPrimaryKey" parameterType="com.baoxian.domain.UserInfo" >
    update UserInfo
    set username = #{userName},age =#{age}
    where id = #{id}
  </update>

  <!--通过ID查找用户-->
  <select id="selectUserById" resultMap="BaseResultMap" parameterType="java.lang.Long" >
    select id,userName,age
    from UserInfo
    where id = #{id}
  </select>

  <!--查询出所有用户-->
  <select id="selectAll" resultMap="BaseResultMap" >
    select id, username,age from userinfo
  </select>

</mapper>

 

四.品位已映射的SQL语句

从上述例子可知,所有的SQL都可以写进相应的XML映射文件中,简单介绍 xml文件中 <mapper>标签 与 <select>标签

<?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.baoxian.dao.UserInfoMapper" >
  
  <!--通过ID查找用户-->
  <select id="selectUserById" resultMap="BaseResultMap" parameterType="java.lang.Long" >
    select id,userName,age
    from UserInfo
    where id = #{id}
  </select>

</mapper>

在上述例子中,通过sqlSession执行select语句:

UserInfo user = sqlSession.selectOne("com.baoxian.dao.UserInfoMapper.selectUserById", 1L);

selectOne方法的第一个参数:com.baoxian.dao.UserInfoMapper.selectUserById , 有点类似于 java中, 使用全限定名调用类中的方法

定位到这条select的语句的流程是 :①根据nameSpace找到对应的映射文件 ②根据要执行的sql语句id 找到相应的标签

所以要保证每个映射文件的 nameSpace不同,即唯一,同时保证每个映射文件中的 id 不同

 

通过Mapper实例执行SQL语句:

UserInfoMapper mapper = sqlSession.getMapper(UserInfoMapper.class);
UserInfo userInfo = mapper.selectUserById(1L);

此方式,将UserInfoMapper 接口的全限定名  寻找nameSpace 与 该接口全限定名相同的 映射文件. 如果找不到则会报错.  因此一般将nameSpace设置为XxxMapper接口的全限定名

而接口中的方法名 则 与该映射文件中 ,select, insert, update,delete 标签的 ID 进行匹配,

方法中的参数类型 与 该标签中的parameterType对应.

方法的返回值类型 则与 resultType 或 resultMap 对应

方法中形参的名称与sql语句中动态参数名称需相同 ,获取参数值的方式是通过 #{参数名称 或 属性名称} 这种形式获取

 

除了可以通过配置XML 执行sql语句外, 还可以通过在Mapper接口中使用注解达到相同的效果,例如:

//根据user的ID查询User对象
    @Select("select * from UserInfo where id = #{id}")
    public UserInfo queryUserById(Long id);

此效果就相当于映射文件中新增了一下代码:

<!--通过ID查找用户-->
  <select id="queryUserById" resultMap="BaseResultMap" parameterType="java.lang.Long" >
    select * from userInfo where id = #{id}
  </select>

但是不能既使用@select 注解的方式 ,又在映射文件中 加入select 标签的方式 ,否则会运行出错:

Caused by: java.lang.IllegalArgumentException: Mapped Statements collection already contains value for com.baoxian.dao.UserInfoMapper.queryUserById

五. 作用域问题

SqlSessionFactoryBuilder 建议方法作用域

这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但是最好还是不要让其一直存在以保证所有的 XML 解析资源开放给更重要的事情。

SqlSessionFactory : 建议单例模式 应用作用域

SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由对它进行清除或重建。使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏味道(bad smell)”。因此 SqlSessionFactory 的最佳作用域是应用作用域。有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。

SqlSession 建议作用域:方法

每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。也绝不能将 SqlSession 实例的引用放在任何类型的管理作用域中,比如 Servlet 架构中的 HttpSession。如果你现在正在使用一种 Web 框架,要考虑 SqlSession 放在一个和 HTTP 请求对象相似的作用域中。换句话说,每次收到的 HTTP 请求,就可以打开一个 SqlSession,返回一个响应,就关闭它。这个关闭操作是很重要的,你应该把这个关闭操作放到 finally 块中以确保每次都能执行关闭。下面的示例就是一个确保 SqlSession 关闭的标准模式:

SqlSession session = sqlSessionFactory.openSession();
try {
  // do work
} finally {
  session.close();
}

 

映射器实例(Mapper Instances)即Mapper接口实例 建议作用域: 方法

映射器是一个你创建来绑定你映射的语句的接口。映射器接口的实例是从 SqlSession 中获得的。因此从技术层面讲,任何映射器实例的最大作用域是和请求它们的 SqlSession 相同的。尽管如此,映射器实例的最佳作用域是方法作用域。也就是说,映射器实例应该在调用它们的方法中被请求,用过之后即可废弃。并不需要显式地关闭映射器实例,尽管在整个请求作用域(request scope)保持映射器实例也不会有什么问题,但是很快你会发现,像 SqlSession 一样,在这个作用域上管理太多的资源的话会难于控制。所以要保持简单,最好把映射器放在方法作用域(method scope)内。下面的示例就展示了这个实践:

SqlSession session = sqlSessionFactory.openSession();
try {
  BlogMapper mapper = session.getMapper(BlogMapper.class);
  // do work
} finally {
  session.close();
}

 

posted @ 2018-07-29 23:15  yi杆烟枪  阅读(218)  评论(0编辑  收藏  举报