lukazan

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

1、依赖

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.1</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.9</version>
</dependency>

2、MyBatis主配置文件

<?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>
 
    <!--指定properties文件的位置,从类路径根开始找文件-->
    <properties resource="jdbc.properties"/>
 
    <!--设置日志级别-->
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING" />
    </settings>
 
    <!--配置数据源-->
    <environments default="mydev">
        <!--id:数据源的名称-->
        <environment id="mydev">
            <!--配置事务类型:使用 JDBC 事务(使用 Connection 的提交和回滚) -->
            <transactionManager type="JDBC"/>
            <!--数据源 dataSource:创建数据库Connection对象  type: POOLED 使用数据库的连接池-->
            <dataSource type="POOLED">
                <!--连接数据库的四个要素-->
                <property name="driver" value="${jdbc.driverClassName}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>
 
    <!--指定mapper.xml文件位置,告诉mybatis要执行的sql语句的位置-->
    <mappers>
        <mapper resource="com/luca/dao/StudentDao.xml"/>
    </mappers>
    <mappers>
        <!--第一种方式:指定多个mapper文件-->
        <!--<mapper resource="com/luca/dao/StudentDao1.xml"/>-->
        <!--<mapper resource="com/luca/dao/StudentDao2.xml"/>-->
        <!--<mapper resource="com/luca/dao/StudentDao3.xml"/>-->
        <!--第二种方式:使用包名
            name:xml文件(mapper文件),这个包中的所有xml文件一次都能加载给mybatis
        -->
        <package name="com.luca.dao"/>
 
    <!--定义别名-->
    <typeAliases>
        <!--起别名的第一种方式:可以指定一个类型的自定义别名
            type:自定义类型的全限定名称
            alias:别名(短小,容易记忆的)
        -->
        <!--<typeAlias type="com.luca.domain.Student" alias="stu"/>-->
        <!--<typeAlias type="com.luca.vo.ViewStudent" alias="vstu"/>-->
 
        <!--第二种方式
            <package> name是包名,这个包中的所有类,类名就是别名(类名不区分大小写)
            下面两个包中的所有类的类名,就是该类的别名
        -->
        <package name="com.luca.domain"/>
        <package name="com.luca.vo"/>
    </typeAliases>
</configuration>
根元素为<configuration>标签,主要包括:1)数据源;2) 指定mapper.xml文件位置;3)定义别名

(1) 配置数据源<dataSource>

  1. <dataSource type="pooled" >的type实现 Mybatis 中连接池的配置。
    • UNPOOLED 不使用连接池的数据源, 实现了 javax.sq.DataSource 接口
    • POOLED 使用连接池的数据源, 实现了 javax.sq.DataSource 接口
    • JNDI 使用 JNDI 实现的数据源
  2. 定义连接数据库的四个要素。
  3. 事务:<transactionManager type="JDBC"/>指定mybatis使用JDBC的事务管理机制。
    • MyBatis支持两种事务管理器类型
      • JDBC:使用JDBC的事务管理机制。
      • MANAGED:由容器来管理事务的整个生命周期(如 Spring 容器)。
    • MyBatis将自动提交功能关闭了,改为了手动提交。设置自动提交:session = factory.openSession(true)

(2) 使用<properties resource="jdbc.properties"/>把数据库属性文件配置进来。

(3)  <mappers>映射器

  1. <mapper resource=""/> 使用相对于类路径的资源,从 classpath 路径查找文件,多个xxxMapper需要一个一个添加
  2. <package name=""/> 指定包下的所有 Dao 接口,一次性把包内的所有xxxMapper加载进来
    • 此种方法要求 Dao 接口名称和 mapper 映射文件名称相同,且在同一个目录中。(与Spring整合不需要此规则)

(4) 定义实体类别名的两种方式

  • <typeAlias type="com.luca.domain.Student" alias="stu"/>     alias的值为别名
  • <package name="com.luca.domain"/>    类名即为别名
  • 为啥用别名:ResultType值为全类名或别名,全类名太长,用别名更方便。

3、编写DAO接口

public interface StudentDao {
    //插入一条学生数据
    int insertStudent(Student student);
    //查询所有数据
    List<Student> selectStudents();
}

4、编写 Dao 接口 Mapper 的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">
 
<!-- namespace:必须有值,自定义的唯一字符串推荐使用: dao 接口的全限定名称 -->
<mapper namespace="com.StudentDao">
    <insert id="insertStudent">
        insert into student values(#{id},#{name},#{email},#{age})
    </insert>
 
    <!--
        <select>: 查询数据, 标签中必须是 select 语句
        id: sql 语句的自定义名称,推荐使用 dao 接口中方法名称,使用名称表示要执行的 sql 语句
        resultType: 查询语句的返回结果数据类型,使用全限定类名
    -->
    <select id="selectStudents" resultType="com.luca.domain.Student">
        select * from student order by id desc
    </select>
</mapper>

5、朴素用法(核心原理)

InputStream is = Resources.getResourceAsStream("mybatis.xml");            //读取配置文件
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();        //获得SqlSessionFactoryBuilder对象为了获得SqlSessionFactory
SqlSessionFactory factory = builder.build(is);                            //创建 SqlSessionFactory 对象,目的是获取 SqlSession
SqlSession sqlSession = factory.openSession();                            //获取 SqlSession,SqlSession 能执行 sql 语句
String sqlId = "com.luca.dao.StudentDao.insertStudent";                   //sqlId是dao接口中方法的全类名,对应mapper文件中的同名sql语句
int res = sqlSession.insert(sqlId, new Student(1004, "赵六", "dhweu@qq.com", 48));    //执行insert方法
sqlSession.commit();                                                      //默认不自动提交事务,需要手动提交

(1)Resources 类(org.apache.ibatis.io.Resources)

        Resources 类,顾名思义就是资源,用于读取资源文件。其有很多方法通过加载并解析资源文件,返回不同类型的 IO 流对象。

(2)SqlSessionFactoryBuilder 类

        SqlSessionFactory 的 创 建 , 需 要 使 用 SqlSessionFactoryBuilder 对 象 的 build() 方 法 。 由 于SqlSessionFactoryBuilder 对象在创建完工厂对象后,就完成了其历史使命,即可被销毁。所以,一般会将该 SqlSessionFactoryBuilder 对象创建为一个方法内的局部对象,方法结束,对象销毁。

(3)SqlSessionFactory 接口

        SqlSessionFactory 接口对象是一个重量级对象(系统开销大的对象),是线程安全的, 所以一个应用只需要一个该对象即可。 创建 SqlSession 需要使用 SqlSessionFactory 接口的的 openSession()方法。
  • openSession(true):创建一个有自动提交功能的 SqlSession
  • openSession(false):创建一个非自动提交功能的 SqlSession,需手动提交
  • openSession():同 openSession(false)

(4) SqlSession 接口

        SqlSession 接口对象用于执行持久化操作。一个 SqlSession 对应着一次数据库会话,一次会话以SqlSession 对象的创建开始,以 SqlSession 对象的关闭结束。SqlSession 接口对象是线程不安全的,所以每次数据库会话结束前,需要马上调用其 close()方法,将其关闭。再次需要会话,再次创建。 SqlSession 在方法内部创建,使用完毕后关闭。
  可将此代码创建为一个util类,通过调用工具类来获取sqlSession
public class MyBatisUtils {
 
    private static SqlSessionFactory factory = null;
    //使用 静态块 创建一次 SqlSessionFactory
    static {
        String config = "mybatis.xml";  //需要和你的项目中的文件名一致
        try {
            InputStream is = Resources.getResourceAsStream(config);
            factory = new SqlSessionFactoryBuilder().build(is);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
    //获取SqlSession的方法
    public static SqlSession getSqlSession() {
        SqlSession session = null;
        if (factory != null) {
            session = factory.openSession(); //非自动提交事务
        }
        return session;
    }
}

6、传统用法

  创建dao接口的实现类,实现接口中的方法。在方法体内调用工具类获取sqlSession对象,调用sqlSession中的方法实现sql操作。
public List<Student> selectStudents() {
    SqlSession session = MyBatisUtil.getSqlSession();
    List<Student> studentList = session.selectList("com.bjpowernode.dao.StudentDao.selectStudents");
    session.close();
    return studentList;
}
分析
  • Dao 的实现类其实并没有干什么实质性的工作,它仅仅就是通过 SqlSession 的相关 API 定位到映射文件 mapper 中相应 id 的 SQL 语句,真正对 DB 进行操作的工作其实是由框架通过 mapper 中的 SQL 完成的。
  • MyBatis 框架就抛开了 Dao 的实现类,直接定位到映射文件 mapper 中的相应 SQL 语句,对DB 进行操作。
  • 这种对 Dao 的实现方式称为 Mapper 的动态代理方式。Mapper 动态代理方式无需程序员实现 Dao 接口。接口是由 MyBatis 结合映射文件自动生成的动态代理实现的。

7、MyBatis 框架 Dao 代理(高级用法)

(1) 去掉 Dao 接口实现类

(2) getMapper() 获取代理对象

StudentDao studentDao = MyBatisUtil.getSqlSession().getMapper(StudentDao.class);

(3) 使用 Dao 代理对象方法执行 sql 语句

StudentDao studentDao = MyBatisUtil.getSqlSession().getMapper(StudentDao.class);
int nums = studentDao.insertStudent(new Student(1004, "赵六", "dhweu@qq.com", 48));

(4) 原理

  1. studentDao为org.apache.ibatis.binding.MapperProxy代理对象
  2. MapperProxy类invoke方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
try {
return method.invoke(this, args);
} catch (Throwable var5) {
throw ExceptionUtil.unwrapThrowable(var5);
}
} else {
MapperMethod mapperMethod = this.cachedMapperMethod(method);
return mapperMethod.execute(this.sqlSession, args);
}
}
  1. MapperProxy类execute方法

public Object execute(SqlSession sqlSession, Object[] args) {
Object param;
Object result;
if (SqlCommandType.INSERT == this.command.getType()) {
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
} else if (SqlCommandType.UPDATE == this.command.getType()) {
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
}....
}

(5) 顶级用法说明(具体使用见SSM整合,目前使用上面的方法即可)

  此方法为MyBatis框架实现DAO代理,还需要自定义工具类获取sqlSession对象,并非顶级用法。顶级用法是Spring整合MyBatis,在Spring配置文件中配置SqlSessionFactoryBean对象、MapperScannerConfigurer对象。自动扫描xxxDao接口和xxxDao.xml的Mapper配置文件。

  升级版:在尚筹网项目中,可以将dao接口与mapper文件分开放,在spring-mybatis配置文件中配置dao接口和mapper文件的位置。

<!--配置SqlSessionFactoryBean整合mybatis-->
    <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <!--配置mybatis全局配置文件-->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <!--指定Mapper.xml配置文件的位置-->
        <property name="mapperLocations" value="classpath:mybatis/mapper/*Mapper.xml"/>
    </bean>

    <!--注册Mapper扫描配置器-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!--指定SqlSessionFactory对象的id-->
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"/>
        <!--指定包名,包名是dao接口所在的包名
            MapperScannerConfigurer会扫描这个包中的所有接口,把每个接口都执行一次getMapper()方法,
            得到每个接口的dao对象。把创建好的dao对象放入到spring的容器中。
        -->
        <property name="basePackage" value="com.atguigu.crowd.mapper"/>
    </bean>

8、参数传递

(0) parameterType

        在mapper文件中使用。用来限定调用sql语句时传入的参数类型。(目前感觉没啥用,以后再研究研究)

(1)  一个简单参数

        Dao 接口中方法的参数只有一个简单类型(java 基本类型和 String),占位符 #{ 任意字符 },和方法的参数名无关。
接口方法:
Student selectById(int id);
 
mapper 文件:
<select id="selectById" resultType="com.bjpowernode.domain.Student">
    select id,name,email,age from student where id=#{studentId}
</select>
#{studentId} , studentId 是自定义的变量名称,和方法参数名无关。

(2)  多个参数-使用@Param

        当 Dao 接口方法多个参数,需要通过名称使用参数。 在方法形参前面加入@Param(“自定义参数名”),mapper文件使用#{自定义参数名}。
接口方法:
List<Student> selectMultiParam(@Param("personName") String name,@Param("personAge") int age);
 
mapper 文件:
<select id="selectMultiParam" resultType="com.bjpowernode.domain.Student">
    select id,name,email,age from student where name=#{personName} or age = #{personAge}
</select>

(3)  多个参数-使用对象

  • 使用 java对象传递参数,对象的属性值就是sql需要的参数值。 每一个属性就是一个参数。
  • 语法格式:#{ property,javaType=java 中数据类型名,jdbcType=数据类型名称 },javaType, jdbcType 的类型 MyBatis 可以检测出来,一般不需要设置。 
  • 常用格式:#{ property }
创建保存参数值的对象 QueryParam
public class QueryParam {
    private String queryName;
    private int queryAge;
    //实现属性的set,get 方法
}
 
接口方法:
List<Student> selectMultiObject(QueryParam queryParam);
 
mapper 文件:
<select id="selectMultiObject" resultType="com.bjpowernode.domain.Student">
    select id,name,email,age from student where name=#{queryName} or age=#{queryAge}
</select><select id="selectMultiObject" resultType="com.bjpowernode.domain.Student">
    select id,name,email,age from student where name=#{queryName,javaType=string,jdbcType=VARCHAR}or age =#{queryAge,javaType=int,jdbcType=INTEGER}
</select>

(4)  多个参数-按位置

  • 参数位置从0开始,引用参数语法 #{arg位置},第一个参数是#{arg0},第二个是#{arg1}。
  • 注意:mybatis-3.3版本和之前的版本使用#{0},#{1}方式,从mybatis3.4开始使用#{arg0}方式。
接口方法:
List<Student> selectByNameAndAge(String name,int age);
mapper 文件
<select id="selectByNameAndAge" resultType="com.bjpowernode.domain.Student">
    select id,name,email,age from student where name=#{arg0} or age =#{arg1}
</select>

 (5)  多个参数-使用 Map

        Map集合可以存储多个值,使用Map向mapper文件一次传入多个参数。Map集合使用String的key,Object类型的值存储参数。mapper文件使用#{key}引用参数值。
Map<String,Object> data = new HashMap<String,Object>();
data.put(“myname”,”李力”);
data.put(“myage”,20);
接口方法:
List<Student> selectMultiMap(Map<String,Object> map);
mapper 文件:
<select id="selectMultiMap" resultType="com.bjpowernode.domain.Student">
    select id,name,email,age from student where name=#{myname} or age =#{myage}
</select>

(6) 占位符#和$

  • #:告诉 mybatis 使用实际的参数值代替。使用 PrepareStatement 对象执行 sql 语句, #{…}代替sql 语句的“?”。 这样做更安全,更迅速,通常也是首选做法,
  • $:字符串替换,告诉mybatis使用$包含的“字符串”替换所在位置。使用 Statement 把 sql 语句和${}的内容连接起来。主要用在替换表名,列名,不同列排序等操作

9、封装 MyBatis 输出结果

(1)  resultType

  执行sql得到ResultSet转换的数据类型,使用类型的全限定名称或别名(别名的设置上第二点“主配置文件说明”)。

  1. 返回对象类型时:mybatis框架将ResultSet转换为指定的对象类型,使用类的空参构造方法创建对象,使用setXXX给属性赋值。
    • 复习:类没有显示定义构造方法,默认有空参构造方法。如果显示了定义带参构造方法,则没有空参构造函数了。
    • 所以要实现转换,1)类要有空参构造函数,2)要实现setXXX方法。
  2. 返回集合类型时:ResultType应该设置为集合包含的类型,而不是集合本身。resultType和resultMap,不能同时使用。
  3. 返回Map类型时:sql 的查询结果作为 Map 的 key 和 value。推荐使用 Map<Object,Object>。
    • 注意: Map 作为接口返回值, sql 语句的查询结果最多只能有一条记录。 大于一条记录是错误。
接口方法:
Map<Object,Object> selectReturnMap(int id);
 
mapper 文件:
<select id="selectReturnMap" resultType="java.util.HashMap">
    select name,email from student where id = #{studentId}
</select>

(2)  resultMap

        resultMap 可以自定义 sql 的结果和 java 对象属性的映射关系。更灵活的把列值赋值给java对象的属性。常用在列名和 java对象属性名不一致的情况。
<!--定义resultMap   id:自定义名称,表示定义的resultMap    type:java类型的全限定名称-->
<resultMap id="studentMap" type="com.luca.domain.Student">
    <!--列名和java属性关系-->
    <!--主键类,使用id标签  column:列名  property:java类型的属性名-->
    <id column="id" property="id"/>
    <!--非主键,使用result标签  column:列名  property:java类型的属性名-->
    <result column="name" property="name"/>
    <result column="email" property="email"/>
    <result column="age" property="age"/>
</resultMap>
<!--resultMap: resultMap 标签中的 id 属性值-->
<select id="selectAllStudents" resultMap="studentMap">
    select id,name,email,age from student
</select>

(3)  实体类属性名和列名不同的处理方式

  1. 使用列别名和<resultType>,在mapper文件的sql语句添加列别名,列别名为java对象属性名
<select id="selectUseFieldAlias" resultType="com.bjpowernode.domain.PrimaryStudent">
    select id as stuId, name as stuName,age as stuAge from student where name=#{queryName} or age=#{queryAge}
</select>
  1. 使用<resultMap>,见上

(4)  模糊 like

  1. java 代码中提供要查询的 “%力%”
接口方法:
List<Student> selectLikeFirst("%力%");
 
mapper 文件:
<select id="selectLikeFirst" resultType="com.bjpowernode.domain.Student">
    select id,name,email,age from studentwhere name like #{studentName}
</select>
  1. mapper 文件中使用 like name "%" #{xxx} "%"
<select id="selectLikeSecond" resultType="com.bjpowernode.domain.Student">
    select id,name,email,age from studentwhere name like "%" #{studentName} "%"
</select>

10、动态SQL

        动态 SQL,通过 MyBatis 提供的各种标签对条件作出判断以实现动态拼接 SQL 语句。这里的条件判断使用的表达式为OGNL表达式。常用的动态SQL标签有<if>、<where>、<choose/>、<foreach>等。MyBatis的动态SQL语句,与JSTL中的语句非常相似。
        动态SQL,主要用于解决查询条件不确定的情况:在程序运行期间,根据用户提交的查询条件进行查询。提交的查询条件不同,执行的SQL语句不同。若将每种可能的情况均逐一列出,对所有条件进行排列组合,将会出现大量的SQL语句。此时,可使用动态SQL来解决这样的问题

(1) <if>

<select id="selectStudentIf" resultType="com.bjpowernode.domain.Student">
    select id,name,email,age from studentwhere 1=1
    <if test="name != null and name !='' ">
        and name = #{name}
    </if>
    <if test="age > 0 ">
       and age > #{age}
    </if>
</select>

(2)  <where>

        <if/>标签的中存在一个比较麻烦的地方:需要在 where 后手工添加 1=1 的子句。如果where 后的所有<if/>条件均为 false,而 where 后若又没有 1=1 子句,则 SQL 中就会只剩下一个空的 where,SQL出错。
        使用<where/>标签, 在有查询条件时,可以自动添加上where子句;没有查询条件时,不会添加where子句。需要注意的是,第一个<if/>标签中的SQL片断,可以不包含and。不过,写上 and也不错,系统会将多出的and去掉。但其它<if/>中SQL片断的and,必须要求写上。否则SQL语句将拼接出错。
<select id="selectStudentWhere" resultType="com.bjpowernode.domain.Student">
    select id,name,email,age from student
    <where>
        <if test="name != null and name !=''">
            and name = #{name}
        </if>
        <if test="age > 0">
            and age > #{age}
        </if>
    </where>
</select> 

(3) <foreach>

         用于实现对于数组与集合的遍历。
  • collection表示要遍历的集合类型list,array等。
  • open、close、separator为对遍历内容的SQL拼接。
语法:
<foreach collection="集合类型" open="开始的字符" close="结束的字符"item="集合中的成员" separator="集合成员之间的分隔符">
    #{item的值即"集合中的成员"}
</foreach>
  1. 遍历 List<简单类型>

需求:查询学生 id 是 1002,1005,1006
接口方法:
List<Student> selectStudentForList(List<Integer> idList);
 
mapper 文件:
<select id="selectStudentForList" resultType="com.bjpowernode.domain.Student">
    select id,name,email,age from student
    <if test="list !=null and list.size > 0 ">
        where id in
        <foreach collection="list" open="(" close=")" item="stuid" separator=",">
            #{stuid}
        </foreach>
    </if>
</select>
  1. 遍历 List<对象类型>

接口方法:
List<Student> selectStudentForList2(List<Student> stuList);
 
mapper 文件:
<select id="selectStudentForList2" resultType="com.bjpowernode.domain.Student">
    select id,name,email,age from student
    <if test="list !=null and list.size > 0 ">
        where id in
        <foreach collection="list" open="(" close=")" item="stuobject" separator=",">
           #{stuobject.id}
        </foreach>
    </if>
</select>

(4) 代码片段

        <sql/>标签用于定义 SQL 片断,以便其它 SQL 标签复用。而其它标签使用该 SQL 片断,需要使用<include/>子标签。该<sql/>标签可以定义 SQL 语句中的任何部分,所以<include/>子标签可以放在动态SQL的任何位置。
<!--创建 sql 片段 id:片段的自定义名称-->
<sql id="studentSql">
    select id,name,email,age from student
</sql>
<select id="selectStudentSqlFragment" resultType="com.bjpowernode.domain.Student">
    <!-- 引用 sql 片段 -->
    <include refid="studentSql"/>
    <if test="list !=null and list.size > 0 ">
        where id in
        <foreach collection="list" open="(" close=")" item="stuobject" separator=",">
            #{stuobject.id}
        </foreach>
    </if>
</select> 

11、PageHelper插件

(1) 依赖与插件

<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.1.10</version>
</dependency>
<plugins>
    <plugin interceptor="com.github.pagehelper.PageInterceptor" />    #在<environments>之前加入
</plugins>

(2)  PageHelper 对象

        在你需要进行分页的 MyBatis 查询方法前调用PageHelper.startPage静态方法即可,紧跟在这个方法后的第一个 MyBatis 查询方法会被进行分页。
public void testSelect() throws IOException {
    //获取第 1 页, 3 条内容
    PageHelper.startPage(1,3);
    List<Student> studentList = studentDao.selectStudents();
    studentList.forEach( stu -> System.out.println(stu));
}

12、MyBatis总结思维导图

 

 

posted on 2021-04-20 14:19  lukazan  阅读(132)  评论(0编辑  收藏  举报