Mybatis从零开始详细讲解

     

框架(Framework)

MyBatis

 

1. MyBatis 简介 1-2

1.1. MyBatis 概述? 1-2

1.1.1. MyBatis 是什么? 1-2

1.1.2. MyBatis 应用场景 1-2

1.1.3. MyBatis 应用优势 1-3

1.2. MyBatis 架构体系? 1-3

1.2.1. MyBatis接口应用层 1-3

1.2.2. MyBatis数据处理层 1-4

1.2.3. MyBatis基础服务层 1-4

1.3. MyBatis 核心组件? 1-4

1.3.1. MyBatis核心文件 1-4

1.3.2. MyBatis 核心API 1-4

2. MyBatis 编程基础 2-5

2.1. MyBatis 基本步骤 2-5

2.1.1. JDBC 编程步骤回顾 2-5

2.1.2. MyBaits 编程步骤分析 2-5

2.2. MyBatis 编程实现 2-6

2.2.1. 数据库数据准备 2-6

2.2.2. 创建Maven桌面项目 2-6

2.2.3. 添加配置及映射文件 2-7

2.2.4. 编写代码执行查询测试 2-10

3. MyBatis 编程进阶 3-13

3.1. MyBatis基于接口实现的基本步骤 3-13

3.2. MyBatis进阶编程实现 3-14

3.2.1. 创建Maven桌面项目 3-14

3.2.2. 创建config.properties文件 3-14

3.2.3. 创建mybatis核心配置文件 3-15

3.2.4. 创建Blog实体对象 3-15

3.2.5. 创建BlogDao接口 3-16

3.2.6. 创建BlogMapper映射文件 3-17

3.2.7. 创建单元测试类执行测试 3-18

3.3. MyBatis业务增强 3-20

3.3.1. 动态排序策略 3-20

3.3.2. ID应用策略 3-21

4. MyBatis 高级应用 4-23

4.1. 日志配置应用(了解) 4-23

4.1.1. 日志基本概述 4-23

4.1.2. 日志基本实现 4-23

4.2. 缓存配置应用 4-24

4.2.1. 缓存基本概述 4-24

4.2.2. 缓存基本实现 4-24

4.3. 高级映射应用 4-28

4.3.1. 高级映射基本概述 4-28

4.3.2. 高级映射基本实现 4-29

4.4. 延迟加载应用 4-31

4.4.1. 延迟加载基本概述 4-31

4.4.2. 延迟加载基本实现 4-32

4.5. 动态SQL应用 4-33

4.5.1. 动态SQL基本概述 4-33

4.5.2. 动态SQL基本实现 4-33

4.6. 应用扩展? 4-35

5. 总结 5-35

5.1. 重点和难点分析 5-35

5.2. 常见FAQ 5-36

5.3. 作业 5-37

 

1. MyBatis 简介

1.1. MyBatis 概述?

1.1.1. MyBatis 是什么?

MyBatis 由apache的ibatis演变而来,可以从如下几个方面加强理解与应用。

 

1) 软件框架(Framework): 用于解决软件中的通用型(共性)问题

2) 软件中的优秀的持久层框架(数据访问层):用于更好解决数据持久化问题

 

课后了解:JAVA生态项目中常用的持久层框架

 

hibernate (ORM 框架) :用于解决数据的持久化问题(数据库操作)

1.1.2. MyBatis 应用场景

MyBatis主要应用于Java技术生态项目的研发。例如:

 

1) Java传统桌面型项目(例如传统的计费系统等)

2) Java web 互联网项目(例如电商项目,企业互联网业务系统)

 

1.1.3. MyBatis 应用优势

思考:

1) 互联网项目的硬性要求是什么?(快速交付)

2) 互联网项目的如何进行技术选型?

第一:稳定,可靠,性能;

第二:学习成本。

第三:是否可持续发展,社区支持的力度)

 

思考:

传统JDBC开发劣势?

 

1) 编程步骤,参数配置及读取相关繁琐(配置信息的读取,都需要自己写代码)

2) 数据直接映射硬编码的强度会比较高(代码冗余相对较高,维护起来困难)

3) 数据关系映射的实现相对困难?(one2one,one2many,many2many)

 

思考:框架MyBatis开发优势

 

1) 封装了JDBC共性,简化了代码的编写,提高了代码的开发速度,以及可维护性。

2) 合理的架构设计,提高了系统的稳定性,访问性能,可扩展性。

 

 

框架MyBatis开发劣势

 

1) SQL语句编写的工作量相对较大。(相对hibernate框架)

2) SQL语句依赖于数据库,移植性相对较差。(不是最大劣势)

1.2. MyBatis 架构体系?

对于任何一个持久层框架,都应该具备接口服务,数据处理服务,基础服务等相关功能,MyBatis也不例外,它会具体如下几个层结构.

 

1.2.1. MyBatis接口应用层

MyBatis接口应用层主要负责对外提供应用服务,例如

 

1) 数据查询

2) 数据修改

3) 数据删除

4) 数据插入

 

1.2.2. MyBatis数据处理层

MyBatis数据处理层主要负责处理数据访问问题

 

1) SQL参数映射(Dao方法参数与映射文件中#{}表达式映射)

2) SQL解析(语法,语义) 例如: select * from blog where id=#{id}

3) SQL 执行(将sql发送到数据库端执行)

4) SQL 结果映射(例如将ResultSet中的数据到map)

5) ....

 

1.2.3. MyBatis基础服务层

MyBatis基础服务层主要负责提供如下几个方面的服务:

 

1) 连接服务 (配置连接池,)

2) 事务服务(保证数据的原子性,一致性,隔离性,一致性。)

3) 缓存服务(更好的提高查询性能)

4) 配置服务 (别名配置,映射配置,...,日志配置,....)

5) .....

1.3. MyBatis 核心组件?

1.3.1. MyBatis核心文件

MyBatis 核心应用组件:

1. 配置文件(提供基础配置信息,例如连接配置,缓存配置,映射配置)

2. 映射文件(定义SQL映射)

 

1.3.2. MyBatis 核心API

MyBatis 项目中核心API:

 

1. SqlSessionFactoryBuilder (负责读取配置文件,创建SqlSessionFactory对象)

2. SqlSessionFactory(负责创建SqlSession对象)

3. SqlSession(负责连接的维护,事务的处理,类似JDBC中的Connection)

4. .....

 

思考:

 

1)核心组件在应用中的一个角色定位?

2)应用底层会用到哪些设计模式?建造模式,工厂模式

 

 

2. MyBatis 编程基础

2.1. MyBatis 基本步骤

2.1.1. JDBC 编程步骤回顾

JDBC 编程的基本步骤如下:

 

1) 加载驱动程序Driver (Class.forName(“com.mysql.jdbc.Driver”))

2) 建立连接Connection (DriverManager.getConnection(url,username,password))

3) 创建Statement (conn.createStatement())

4) 发送sql (stmt.executeUpdate(sql))

5) 处理结果ResultSet (while(rs.next))

6) 释放资源(close)

 

2.1.2. MyBaits 编程步骤分析

MyBatis 项目中一般的编程步骤:

 

Step01:创建maven 桌面项目(Java 项目)

Step02:在pom.xml文件中添加Mybatis依赖和mysql驱动依赖。

Step03:创建mybatis 配置文件,映射文件

Step04:配置数据访问(配置文件),SQL映射(映射文件)

Step05:创建MyBatis API(例如SqlSession)对象,执行SQL操作.

 

思考?

1) 数据库连不上,可能存在哪些问题?

a) 检测url,用户名,密码

b) 检测端口号(port)

c) id地址是否能ping 通

d) 检测驱动程序(有可能依赖的驱动程序与数据库版本不兼容)

 

2) mybatis 配置文件的名字有要求吗?(只要符合标识符规范即可)

3) mybatis 配置文件是要对哪些信息进行配置呢?

4) mybatis 映射文件中主要用于定义哪些内容?(sql元素)

5) MyBatis 执行SQL操作时一个基本过程是怎样的?

a) step01:MyBatis API调用JDBC API

b) step02:JDBC API 调用数据库驱动程序API

 

2.2. MyBatis 编程实现

2.2.1. 数据库数据准备

创建数据库,并在库中创建表

 

create database cgbmybatis character set utf8;

 

打开数据库

 

use cgbmybatis

 

创建表

 

create table blog(

 id int primary key auto_increment,

 title varchar(200) not null,

 content varchar(500) not null,

 createdTime date

) engine=innoDB;

 

向表中写入数据

insert into blog values (null,'ta','ta...',now());

insert into blog values (null,'tb','tb...',now());

 

2.2.2. 创建Maven桌面项目

 

创建maven 桌面项目,并添加依赖(mybatis,mysql驱动)

说明:桌面项目的创建参考doc.tedu.cn中maven配置

 

添加mybatis依赖

<dependency>

  <groupId>org.mybatis</groupId>

  <artifactId>mybatis</artifactId>

  <version>3.2.8</version>

 </dependency>

 

添加MySQL驱动依赖

<dependency>

   <groupId>mysql</groupId>

   <artifactId>mysql-connector-java</artifactId>

   <version>5.1.40</version>

 </dependency>

 

添加junit依赖

<dependency>

   <groupId>junit</groupId>

   <artifactId>junit</artifactId>

   <version>4.12</version>

 </dependency>

 

思考:

1) 添加依赖时注意groupId的选择?(选正规的)

2) 添加依赖以后pom.xml文件假如有错什么原因?

检测网络,检测maven配置(setting.xml)

 

2.2.3. 添加配置及映射文件

在src/main/resources目录下创建配置文件mybatis-configs.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>

  <!-- 配置初始化环境(连接) -->

  <environments default="development">

    <environment id="development">

      <transactionManager type="JDBC"/>

      <dataSource type="POOLED">

        <property name="driver" value="com.mysql.jdbc.Driver"/>

        <property name="url"  value="jdbc:mysql:///cgb1711"/>

        <property name="username" value="root"/>

        <property name="password" value="root"/>

      </dataSource>

    </environment>

  </environments>

  <!-- 配置mapper文件路径 -->

  <mappers>

     <mapper resource="mapper/BlogMapper.xml"/>

  </mappers>

</configuration>

 

说明:

1)配置文件的头可从官方文档进行拷贝.

2)配置文件元素没有提示是什么原因? 对应的dtd文件取不到

提示信息的配置请参考:http://schema.tedu.cn/proxy/

 

step02:在src/main/resources/mapper目录下创建映射文件BlogMapper.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.jt.blog.BlogDao">

 

</mapper>

 

说明:

1) 映射文件的命名空间用于约束元素id的唯一性.

2) 映射文件的格式最好是包结构形式

3) 映射文件内部可以定义很多元素,每个元素必须有一个唯一id,例如select

 

 

在mapper文件中添加如下元素:

 

查询所有blog数据

<select id="findBlogs" resultType="map">

         select * from blog

</select>

 

根据id查询blog数据

<select id="findBlogById" resultType="map">

         select *

         from blog 

         where id=#{id}

</select>

 

限制查询blog元素(此方式参数的传递应用Object[]数组进行封装)

<select id="findPageBlogs"

            resultType="map">

          select *

          from blog

          limit #{array[0]},#{array[1]}

</select>

 

限制查询Blog元素(此方式的参数应用map进行封装,#{}表达式中的内容为map中的key)

 

<select id="findPageBlogs"

            resultType="map">

          select *

          from blog

          limit #{startIndex},#{pageSize}

</select>

 

向表中插入数据元素定义

方案1:(测试时参数数据可以为Object[]类型数组)

   <insert id="insertObject">

          insert into blog

          (id,title,content,createdTime)

          values

          (null,#{array[0]},#{array[1]},now())

    </insert>

 

方案2:(测试时参数数据可以为Blog类型对象或Map对象)

   <insert id="insertObject">

          insert into blog

          (id,title,content,createdTime)

          values

          (null,#{title},#{content},now())

    </insert>

 

 

 

修改表中数据元素定义

方案1:(测试时参数传递需要为Object[]类型的数组)

<update id="updateObject">

         update blog 

         set title=#{array[0]},

             content=#{array[1]}

         where id=#{array[2]}

</update>

 

方案2 (测试时参数传递可以是map或者blog对象)

 

<update id="updateObject">

         update blog 

         set title=#{title},

             content=#{content}

         where id=#{id}

</update>

 

 

 

删除表中元素元素定义

 

  <delete id="deleteObject">

         delete

         from blog

         where id=#{id}

    </delete>

 

 

2.2.4. 编写代码执行查询测试

基于BlogMapper.xml文件中元素的定义,添加测试类及相关方法.

 

编写测试类并添加测试方法

public class TestBlog01 {

private SqlSessionFactory factory;

}

 

测试类中添加初始化factory的方法

@Before

public void init()throws IOException{

//初始化SqlSessionFactory

factory=new SqlSessionFactoryBuilder()

.build(Resources.getResourceAsStream(

"mybatis-configs.xml"));

}

 

测试类中添加查询所有blogs的方法

@Test

public void testFindBlogs(){

//1.创建SqlSession对象(相当于创建一个连接)

SqlSession session=factory.openSession();

//2.执行查询操作(selectList("命名空间"+"元素id"))

List<Object> list=session.selectList(

"com.jt.blog.BlogDao.findBlogs");

for(Object o:list){

System.out.println(o);

}

//3.释放资源(类似关闭连接)

session.close();

}

 

测试类中添加根据id执行查询方法

 

@Test

public void testFindBlogById(){

//1.创建session对象

SqlSession session=

factory.openSession();

//2.执行sql操作

String statement="com.jt.blog.BlogDao.findBlogById";

Map<String,Object> map=

session.selectOne(statement,1);

System.out.println(map);

//3.释放资源(关闭session对象)

session.close();

}

 

测试类中添加分页查询方法

 

@Test

public void testFindPageBlogs(){

//1.创建session对象

SqlSession session=

factory.openSession();

//2.执行sql操作?????

String statement="com.jt.blog.BlogDao.findPageBlogs";

Object parameter=new Object[]{0,4};

List<Map<?,?>> list=

session.selectList(statement, parameter);

for(Map<?,?>map:list){

System.out.println(map);

}

//3.释放资源(关闭session对象)

    session.close();

};

 

 

测试类中添加插入数据的方法

@Test

public void testInsertObject(){

//1.创建session

SqlSession session=factory.openSession();

//2.执行sql

String statement="com.jt.blog.BlogDao.insertObject";

Object parameter=new Object[]{"te","te..."};

int rows=session.insert(statement, parameter);

System.out.println("insert.rows="+rows);

session.commit();

//3.关闭session

session.close();

}

 

测试类中添加修改方法

@Test

public void testUpdateObject(){

//1.创建session

SqlSession session=factory.openSession();

//2.执行sql

String statement="com.jt.blog.BlogDao.updateObject";

Object parameter=new Object[]{"taa","taa...",1};

int rows=session.update(statement, parameter);

System.out.println("update.rows="+rows);

session.commit();

//3.关闭session

session.close();

}

 

测试类中添加删除方法

@Test

public void testDeleteObject(){

//1.创建session

SqlSession session=factory.openSession();

//2.执行sql

String statement="com.jt.blog.BlogDao.deleteObject";

Object parameter=7;

int rows=session.delete(statement, parameter);

session.commit();

System.out.println("delete.rows="+rows);

//3.关闭session

session.close();

}

 

 

总结

 

1)在以上测试时都是调用了SqlSession对象的方法实现了对数据库数据的增删改查操作。例如(selectList,selectOne,insert,update,delete,…..)

2)Mybatis对于insert,update,delete操作的事务控制方式为手动方式,所以再执行完具体操作以后需要执行commit操作。

3. MyBatis 编程进阶

本小节中会从mybatis编程的另一个角度(例如基于接口方式等)实现对数据库中数据的操作.

3.1. MyBatis基于接口实现的基本步骤

 

Step01: 创建maven桌面项目并添加依赖

Step02: 创建配置文件config.propertis(内容为数据库相关)

Step03: 创建mybatis核心配置文件mybatis-configs.xml文件

Step04: 配置Mybatis基础数据服务(properties,datasource,mapper)

Step05: 创建映射文件BlogMapper.xml

Step06: 创建实体类Blog(与表对应,可用于封装表中数据)

Step07: 创建BlogDao接口,并添加相关方法.

Step08: 配置BlogMapper映射文件,添加相关元素.

Step09: 基于BlogDao接口与映射文件实现CRUD操作

 

3.2. MyBatis进阶编程实现

3.2.1. 创建Maven桌面项目

创建maven桌面项目并添加依赖

 

 

<!-- 添加mybatis依赖 -->

 <dependency>

  <groupId>org.mybatis</groupId>

  <artifactId>mybatis</artifactId>

  <version>3.2.8</version>

 </dependency>

 

 <!-- 添加mysql驱动依赖 -->

 <dependency>

   <groupId>mysql</groupId>

   <artifactId>mysql-connector-java</artifactId>

   <version>5.1.40</version>

 </dependency>

 

  <!-- 添加junit依赖 -->

 <dependency>

   <groupId>junit</groupId>

   <artifactId>junit</artifactId>

   <version>4.12</version>

 </dependency>

 

 

 

3.2.2. 创建config.properties文件

在src/main/resources目录下创建config.properties文件,此文件中定义

系统中的一些常用配置信息,例如访问数据库的相关信息,其内容如下

 

driver=com.mysql.jdbc.Driver

url=jdbc:mysql://localhost:3306/cgbmybatis

username=root

password=root

 

3.2.3. 创建mybatis核心配置文件

在src/main/resources目录下创建mybatis-configs.xml文件,并配置数据源等相关信息,数据信息从config.properties文件读取.

 

<?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="config.properties"/>

  <!-- 配置初始化环境(连接) -->

  <environments default="development">

    <environment id="development">

      <transactionManager type="JDBC"/>

      <dataSource type="POOLED">

        <property name="driver" value="${driver}"/>

        <property name="url"  value="${url}"/>

        <property name="username" value="${username}"/>

        <property name="password" value="${password}"/>

      </dataSource>

    </environment>

  </environments>

</configuration>

 

3.2.4. 创建Blog实体对象

创建Blog类实现与数据库中Blog表实现映射.

 

public class Blog {

private Integer id;

private String title;

private String content;

private Date createdTime;

 

public Integer getId() {

return id;

}

public void setId(Integer id) {

this.id = id;

}

public String getTitle() {

return title;

}

public void setTitle(String title) {

this.title = title;

}

public String getContent() {

return content;

}

public void setContent(String content) {

this.content = content;

}

public Date getCreatedTime() {

return createdTime;

}

public void setCreatedTime(Date createdTime) {

this.createdTime = createdTime;

}

@Override

public String toString() {

return "Blog [id=" + id + ", title=" + title + ", content=" + content + ", createdTime=" + createdTime + "]";

}

}

 

3.2.5. 创建BlogDao接口

创建数据访问接口,并添加相关方法

 

 

package com.jt.dao;

import java.util.List;

import org.apache.ibatis.annotations.Param;

import com.jt.entity.Blog;

public interface BlogDao {

 /***

  * 根据id进行对象查找

  * @param id

  * @return

  */

 Blog findBlogById(Integer id);

 List<Blog> findPageBlogs(

     @Param("offset")Integer offset,

     @Param("pageSize")Integer pageSize);

 int insertObject(Blog blog);

 int updateObject(Blog blog);

 int deleteObject(Integer id);

}

 

3.2.6. 创建BlogMapper映射文件

<?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.jt.dao.BlogDao">

 

</mapper>

 

 

BlogMapper文件中添加与BlogDao接口对应的映射元素

 

添加基于ID进行查询的元素

<select id="findBlogById"

           parameterType="int"

           resultType="blog">

           select *

           from blog

           where id=#{id}

   </select>

 

添加分页查询元素

 

  <select id="findPageBlogs"

           resultType="blog">

           select *

           from blog

           limit #{offset},#{pageSize}

   </select>

 

添加insert元素

   <insert id="insertObject"

           parameterType="blog">

           insert into blog

           (id,title,content,createdTime)

           values

           (null,#{title},#{content},now())

   </insert>

 

添加更新元素

   <update id="updateObject"

           parameterType="blog">

           update blog

           set title=#{title},content=#{content}

           where id=#{id}

   </update>

 

添加删除元素

<delete id="deleteObject"

           parameterType="int">

           delete from blog where id=#{id}

</delete>

 

最后在mybatis-configs.xml中添加BlogMapper文件

 

3.2.7. 创建单元测试类执行测试

创建单元测试类,并添加相关方法实现基于Dao接口方式的数据库操作.

 

public class TestBlog01 {

 

private SqlSessionFactory factory;

 

@Before

public void init()throws IOException{

factory=new SqlSessionFactoryBuilder()

.build(Resources.getResourceAsStream(

"mybatis-configs.xml"));

}

@Test

public void testFindBlogById(){

//1.创建session

    SqlSession session=factory.openSession();

//2.执行sql

    //2.1获取dao对象

    BlogDao dao=

    session.getMapper(BlogDao.class);

    //2.2执行dao中方法

    Blog blog=dao.findBlogById(1);

    System.out.println(blog);

//3.关闭session

    session.close();

}

@Test

public void testFindPageBlogs(){

//1.创建session

SqlSession session=factory.openSession();

//2.执行sql

//2.1获取dao对象

BlogDao dao=

session.getMapper(BlogDao.class);

//2.2执行dao中方法

List<Blog> list=dao.findPageBlogs(0, 2);

for(Blog b:list){

System.out.println(b);

}

//3.关闭session

session.close();

}

@Test

public void testInsertObject(){

//1.创建session

SqlSession session=factory.openSession();

//2.执行sql

//2.1获取dao对象

BlogDao dao=

session.getMapper(BlogDao.class);

//2.2执行dao中方法

Blog blog=new Blog();

blog.setTitle("te");

blog.setContent("te...");

dao.insertObject(blog);

session.commit();

//3.关闭session

session.close();

}

 

}

 

3.3. MyBatis业务增强

 

3.3.1. 动态排序策略

 

1. 如何按照用户指定业务对数据进行排序?

 

例如:

1) 博客系统

a) 按照创建时间对博客信息进行排序查询

b) 按照博客访问量对信息进行排序查询

 

2) 电商系统

a) 按照商品价格对商品信息排序

b) 按照商品销量对商品信息排序

c) 按照商品好评对商品信息排序

 

解决方案:

1) 写多个sql映射

2) 写一个sql 映射,然后动态传参,借助${}表达式获取参数

 

例如

接口中方法定义

 

List<Blog> findBlogs(

 @Param("column") String column,

@Param("seq") String seq);

 

映射文件中的实现

 

<select id="findBlogs" 

           resultType="blog">

         select *

         from blog

         order by ${column} ${seq}

</select>

 

思考:

MyBatis中$与#有什么不同?

${}表达式主要用于获取配置文件数据,DAO接口中的参数信息,当$出现在映射文件的SQL语句中时创建的不是预编译的SQL,而是字符串的拼接,有可能会导致SQL注入问题.所以一般使用$接收dao参数时,这些参数一般是字段名,表名等,例如order by {column}.

#{}表达式主要用户获取DAO中的参数数据,在映射文件的SQL语句中出现#{}表达式,底层会创建预编译的SQL.性能会相对较好.

${}获取DAO参数数据时,参数必须使用@param注解进行修饰或者使用下标或者参数#{param1}形式.

#{}获取DAO参数数据时,假如参数个数多于一个可有选择的使用@param.

  

  

3.3.2. ID应用策略

1.保存数据时获取数据在数据库对应的主键值?

 

例如

1) 订单系统?

保存订单信息时候,获取订单在数据库中的主键id值?因为还要依据这个值保存订单的详情信息(例如OrderItem,…..)

2) 权限系统

保存用户信息时,获取用户信息在数据库中的主键值?还有保存用户和权限关系数据

 

   解决方案: 当对象对应的表中的记录为自增长时,可以采用如下方案

 

      <insert id="insertObject"

           parameterType="blog"

           useGeneratedKeys="true"

           keyProperty="id">

           insert into blog

           (id,title,content,createdTime)

           values

           (null,#{title},#{content},now())

   </insert>

   

其中:keyProperty属性用于指定参数中的id属性.

 

2. 当多线程并发的向表中写入数据时,假如id使用自增长可能存在线程安全问题?

例如:

1) 秒杀系统(电商系统)

2) 订票系统(12306)

3) ....

解决方案:可将自增长的id设置为随机数,当然有些数据库根本就不支持自增长,此时也可以选择随机数.

 

课堂案例实现:

 

Step01:数据准备:创建Author表

create table Author(

  id varchar(200) primary key,

  username varchar(100) unique not null,

  password varchar(300) not null,

  email varchar(50) unique

)engine=InnoDB;

   Step02:基于表创建实体类Author

   Step03:基于实体类创建AuthorDao接口

   Step04:基于AuthorDao接口创建映射文件

借助mybatis 应用向表中写入数据,主键值要求通过UUID生成.其映射文件参考

<insert id="insertObject" parameterType="author">

      <selectKey keyProperty="id" 

                  resultType="string"

                  order="BEFORE">

          select replace(uuid(),'-','')

      </selectKey>

       insert into author

       (id,username,password,email)

       values

       (#{id},#{username},#{password},#{email})

</insert>

 

  Step05 编写单元测试

 

说明:

1) 此方式的实现依赖于数据库,存在一定局限性,例如当前这种写法仅适用于mysql

2) 假如不想对数据库有较大依赖,可在应用程序中,通过java API创建随机ID(例如,可借助UUID类产生一个随机串)。

 

4. MyBatis 高级应用

4.1. 日志配置应用(了解)

4.1.1. 日志基本概述

 

Mybatis内置的日志工厂提供日志功能,具体的日志实现有以下几种工具:

 

1)SLF4J(日志框架标准,类似JDBC标准)

2)Apache Commons Logging

3)Log4j 2 (是log4j的升级版,配置文件升级为xml格式了)

4)Log4j(日志处理库,配置文件格式为.properties)

5)JDK logging

 

项目中mybatis通常会借助第三方日志库进行日志的处理,例如log4j.

 

4.1.2. 日志基本实现

配置步骤:(以log4j为例)

1. 添加log4j依赖(一代)

 

  <dependency>

<groupId>log4j</groupId>

<artifactId>log4j</artifactId>

<version>1.2.17</version>

  </dependency>

 

2. 添加log4j.properties 配置(可以从其它项目中拷贝)

 

log4j.rootLogger=INFO,stdout

log4j.appender.stdout=org.apache.log4j.ConsoleAppender

log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

log4j.appender.stdout.layout.ConversionPattern=%d [%-5p] %c - %m%n

 

log4j.logger.com.mybatis3=DEBUG

log4j.logger.com.jt=DEBUG

 

3. 设置mybatis的日志实现(mybatis-configs.xml)

 

  <settings>

     <setting name="logImpl" value="log4j"/>

  </settings>

 

其中name属性的值为固定写法,value的值要依托于使用的日志处理库.

 

说明:课后了解常用的日志处理库.

4.2. 缓存配置应用

4.2.1. 缓存基本概述

1. 缓存是什么? 内存中的一个对象(容器).

2. 缓存对象的作用?提高程序的性能(最主要的的是访问效率)

3. MyBatis 中缓存概述?

MyBatis 框架中提供了非常强大的缓存特性来提高查询性能,通常可将其分为一级缓存(SqlSession级别)和二级缓存(SqlSessionFactory级别)。

 

4.2.2. 缓存基本实现

MyBatis 一级缓存应用:

 

MyBatis中一级缓存默认是开启的.不需要任何配置.例如:

 

测试方案1:(验证一级缓存)

 

@Test

public void testFirstLevelCache01(){

SqlSession session=factory.openSession();

AuthorDao dao=session.getMapper(AuthorDao.class);

String id="5e5f90ed324e11e8a9b279bf5362f090";

dao.findAuthorById(id);

dao.findAuthorById(id);//本次查询从缓存取

session.commit();

session.close();

}

 

测试方案1:(验证缓存失效)

 

验证数据插入导致的缓存失效。

 

@Test

public void testFirstLevelCache01(){

SqlSession session=factory.openSession();

AuthorDao dao=session.getMapper(AuthorDao.class);

String id="5e5f90ed324e11e8a9b279bf5362f090";

dao.findAuthorById(id);

Author entity=new Author();

entity.setUsername("user-KK");

entity.setPassword("123456");

entity.setEmail("KK@t.com");

dao.insertAuthor(entity);

dao.findAuthorById(id);

session.commit();

session.close();

}

 

验证更新后的缓存失效。

 

@Test

public void testFirstLevelCache02(){

SqlSession session=factory.openSession();

AuthorDao dao=session.getMapper(AuthorDao.class);

String id="5e5f90ed324e11e8a9b279bf5362f090";

Author a1=dao.findAuthorById(id);

System.out.println(a1);

Author a=new Author();

a.setId(id);

a.setUsername("user-aaa");

a.setEmail("aaa@t.com");

dao.updateAuthor(a1);

Author a2=dao.findAuthorById(id);

System.out.println(a2);

session.commit();

session.close();

}

 

验证缓存脏读问题(此问题可通过配置缓存类型为STATEMENT类型解决)

 

@Test

public void testFirstLevelCache03(){

SqlSession session1=factory.openSession(true);

SqlSession session2=factory.openSession(true);

AuthorDao dao1=session1.getMapper(AuthorDao.class);

AuthorDao dao2=session2.getMapper(AuthorDao.class);

String id="5e5f90ed324e11e8a9b279bf5362f090";

Author a1=dao1.findAuthorById(id);

System.out.println(a1);

Author a=new Author();

a.setId(id);

a.setUsername("user-oo");

a.setEmail("oo@t.com");

int rows=dao2.updateAuthor(a);

session2.close();

Author a2=dao1.findAuthorById(id);

System.out.println(a2);

session1.close();

}

 

 

 

MyBatis:一级缓存应用说明:

 

1) 默认是开启的(也是应用最多的一种)

2) 其类型为SESSION或STATEMENT两种,默认是SESSION类型

a) 缓存对象的生命周期不同(例如session类型的一级缓存,session关闭就失效.)

b) 其类型的配置可在配置文件中通过这个localCacheScope属性进行配置。

3) 一级缓存在每次更新后都会失效。

4) 一级缓存在事务并发执行时可能会出现脏读,但相对于STATEMENT效率会高一些。

 

MyBatis 二级缓存应用:

 

MyBatis 二级缓存默认是没有开启的,需要在映射文件中加上<Cache/>元素

MyBatis 二级缓存应用步骤:

Step01: 修改mybatis核心配置文件,添加缓存设置.

 

<settings>

     <setting name="cacheEnabled" value="true"/>

  </settings>

 

Step02: 在映射文件中配置Cache策略.

 

 <cache

     eviction="LRU"

     flushInterval="60000"

     size="512"

     readOnly="true"/>

 

这个表示创建了一个 LRU缓存,并每隔 60 秒刷新,存数结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此在不同线程中的调用者之间修改它们会 导致冲突。其中:

 

1) eviction 表示回收策略(例如LRU,FIFO等,默认为LRU)

2) flushInterval 表示刷新间隔

3) size(引用数目)可以被设置为任意正整数,要记住你缓存的对象数目和你运行环境的 可用内存资源数目。默认值是 1024。

4) readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。可读写的缓存会返回缓存对象的拷贝(通过序列化) 。这会慢一些,但是安全,因此默认是 false。

 

Step03: 使用二级缓存了.

 

@Test

public void testSecondLevelCache01(){

SqlSession session1=factory.openSession();

SqlSession session2=factory.openSession();

SqlSession session3=factory.openSession();

AuthorDao dao1=session1.getMapper(AuthorDao.class);

AuthorDao dao2=session2.getMapper(AuthorDao.class);

AuthorDao dao3=session3.getMapper(AuthorDao.class);

String id="5e5f90ed324e11e8a9b279bf5362f090";

Author a1=dao1.findAuthorById(id);

System.out.println(a1);

session1.close();//session关闭时,a1指向对象会存储到缓存

Author a2=dao2.findAuthorById(id);//从二级缓存获取

System.out.println(a2);

a2.setUsername("user-tedu-01");

Author a3=dao3.findAuthorById(id);//从二级缓存获取

System.out.println(a3);

System.out.println(a1==a2);//true

System.out.println(a2==a3);//true

//a1,a2,a3指向的是同一个对象

session2.close();

session3.close();

}

 

FAQ?

1)MyBatis 二级缓存cache元素readOnly值的设置以及底层对象缓存过程.

 

 

 

 

readOnly说明:

 

readOnly的值为true时,缓存中保存的是堆内存中对象的引用.每次从缓存取数据都是获得的同一个对象.readOnly为false时,首先会将查询到的对象,拷贝到缓存一份(对象需要实现序列化接口),然后从缓存取数据每次都是执行对象的拷贝.

 

4.3. 高级映射应用

 

4.3.1. 高级映射基本概述

 

MyBatis中的高级映射一般要借助select元素中的resultMap属性进行实现,通过此属性配置实现一对一,一对多等关系映射的实现.

 

1. 如何实现one2one映射?

2. 如何实现one2many映射?

3. 如何实现many2many映射?

 

 

思考:

1) 查询博客信息时,将作者信息也查询出来?(many2one)

2) 查询某个用户的同时,将其对应的博客列表信息也查询出来(one2many)

3) .....

 

实现方案:

1) 多次单表查询(例如先获取博客信息,再根据外键获取作者信息)

2) 一次关联查询(例如借助 join … on …..等这样的方式实现关联数据的查询)

 

 

4.3.2. 高级映射基本实现

课堂案例:(查询博客信息时将作者信息也查询出来)

 

1) 在blog表中添加一个字段,名字为authorId,类型为string.

alter table blog add authorId varchar(200);

2) 为blog表中的authorId字段赋值,其值为author表中某些记录的主键值.

update blog set authorId=? where id=?

3) 查询某个blog信息,并将其关联的author对象信息查询出来.

 

方案一:

Step01: BlogDao接口中方法定义:

 

Map<String,Object> findBlogAuthorById(Integer id);

 

Step02: BlogMapper中映射元素定义:

 

   <select id="findBlogAuthorById"

           resultType="map">

          select b.*,a.username,a.email

          from blog b join author a

          on b.authorId=a.id

          where b.id=#{id}        

   </select>

 

优势:

1)映射文件中的代码量相对较小,存储简单。

缺陷:

1) 可读性相对较差(map中究竟存储哪里数据,需要通过输出才知道)

2) 从空间使用上可能会有一定的资源浪费

 

方案二:

 

Step01:BlogResult 值对象定义(借助此对象封装多表数据)

 

public class BlogResult {

 

private Integer id;

private String title;

private String content;

private Date createdTime;

private Author author;

    ...set/get

}

 

Step02:BlogDao接口中方法定义:

 

BlogResult findBlogResultById(Integer id);

 

Step03:BlogMapper中映射元素定义

 

   <select id="findBlogResultById"

           resultMap="blogResult">

          select b.*,a.username,a.email

          from blog b join author a

          on b.authorId=a.id

          where b.id=#{id}        

   </select>

 

<resultMap id="blogResult" type="com.jt.vo.BlogResult">

     <id property="id" column="id"/>

     <result property="title" column="title"/>

     <result property="content" column="content"/>

     <result property="createdTime" column="createdTime"/>

     <!-- 关联映射 -->

     <association property="author" javaType="author">

        <id property="id" column="authorId"/>

        <result property="username" column="username"/>

        <result property="email" column="email"/>

     </association>

   </resultMap>

 

优势:

空间利用率有可能会有所提升,对象可读性有一定成都的提升。

缺陷:

映射文件中的代码量相对较大,映射相对复杂,可读性不太好。

 

Step04: 单元测试

 

方案三:(通过两次查询实现)

 

Step01: 接口中方法定义

 

BlogResult findBlogResultWithId(Integer id);

 

Step02: 映射文件元素定义

 

<resultMap type="com.jt.vo.BlogResult" id="blogResultMap">

     <association property="author" 

                    column="authorId"

                    javaType="author"

                    select="com.jt.dao.AuthorDao.findObjectById">

      </association>

 </resultMap>

 <select id="findBlogResultWithId" resultMap="blogResultMap">

       select * from blog where id=#{id}        

 </select>

 

在这个元素映射时假如表中的字段名与类中的属性名相同,可以不使用id,result元素定义具体映射。

此形式的映射关系定义底层会发起两次查询获取blog以及它关联的author数据信息。

 

Step03: 基于DAO接口方法进行单元测试

 

方案优势:

1) 映射文件中代码量小了,可维护性提高了。

2) 可通过配置实现延迟加载(在延迟加载小节会讲到),让查询更加灵活。

方案劣势

1) 一个结果需要多次查询,假如所有数据需要立刻加载,性能会相对较低。

 

4.4. 延迟加载应用

4.4.1. 延迟加载基本概述

延迟加载是按需加载的一种实现策略,一般应用与关联查询中,针对关联对象数据进行延迟加载(何时需要何时加载).以提高内存的使用效率。

4.4.2. 延迟加载基本实现

 

Step01: 添加依赖(CGLIB依赖,底层延迟加载,构建代理对象时需要)

 

<dependency>

  <groupId>cglib</groupId>

  <artifactId>cglib</artifactId>

  <version>3.2.5</version>

</dependency>

 

 

Step02: 配置全局延迟加载(当有些查询不需要延迟加载,可以在查询对应属性中使用fetchType进行覆盖性设置,例如值为lazy表示延迟加载,eager表示实时加载)

 

<settings>

     ....

     <!-- 配置全局延迟加载 -->

     <setting name="lazyLoadingEnabled" value="true"/>

     <!-- 设置按需加载(配合上面的延迟加载属性实现) -->

     <setting name="aggressiveLazyLoading" value="false"/>

  </settings>

 

Step03: 关联映射中就可以直接使用延迟加载了.例如

 

<resultMap type="com.jt.vo.BlogResult"

              id="blogResultMap">

       <association property="author" 

                    column="authorId"

                    javaType="author"

        select="com.jt.dao.AuthorDao.findObjectById">

      </association>

   </resultMap>

   

   <select id="findBlogResultWithId"

           resultMap="blogResultMap">

       select * from blog where id=#{id}        

   </select>

 

当在测试类中只访问blog信息时,不会查询author信息,创建author对象.除非再

在association元素中设置fetchType属性.

 

基于如上配置编写一个单元测试。

 

@Test

public void testFindBlogResultWithId(){

//1.创建SqlSession对象

SqlSession session=factory.openSession();

//2.执行sql查询

BlogDao dao=session.getMapper(BlogDao.class);

BlogResult result=dao.findBlogResultWithId(2);

System.out.println(result.getId());

System.out.println(result.getTitle());

//System.out.println(result.getAuthor());

//3.释放资源

session.close();

}

 

4.5. 动态SQL应用

4.5.1. 动态SQL基本概述

思考:

如何动态删除客户端选择的记录,用户有可能会选择一个记录,也可能会选择多个记录然后进行删除.那么在mybatis的映射文件中该如何配置,如何实现或者说解决这种动态需求问题?

 

解决方案:借助动态sql实现。

 

4.5.2. 动态SQL基本实现

 

动态sql.(以更加灵活的方法解决业务中的数据操作问题)

 

MyBatis 中常用动态sql元素:foreach,if,where,trim,.....

 

本小结以foreach为案例,实现一个动态删除操作,其步骤如下:

 

step01:BlogDao接口中方法定义

 

int deleteObjectByIds(

@Param("ids") String[] ids);

 

step02:BlogMapper文件中定义删除元素

 

<delete id="deleteObjectByIds"

           parameterType="string">

         delete from blog

         where id in  <!-- (10,11)-->

         <!-- 动态sql forearch

collection 的值为dao传递的参数

item为集合中的某一个元素

separator 用于指定分隔符

-->

         <foreach collection="ids"

                  open="("

                  close=")"

                  separator=","

                  item="item">

                  #{item}

         </foreach>

 

</delete>

 

说明:当在foreach 属性直接使用ids接收dao中参数数据时,dao接口方法应

使用@Param注解进行声明.假如没有使用注解声明可以使用 array接收到方法中参数数组数据

 

step03:编写单元测试

 

@Test

public void testDeleteObjectByIds(){

//1.创建session

SqlSession session=factory.openSession();

//2.执行sql

//2.1获取dao对象

BlogDao dao=session.getMapper(BlogDao.class);

    int rows=

    dao.deleteObjectByIds(new String[]{"9"});

    System.out.println("delete.rows="+rows);

    session.commit();

   //3.关闭session

    session.close();

}

 

课后尝试:

完成一个批量插入的操作?

 

 

4.6. 应用扩展?

课后了解:(本阶段不作为重点掌握)

1) MyBatis 中拦截器?

2) MyBatis 中分页插件应用?

3) MyBatis 中通用mapper的实现?

3) Mybatis 中逆向工程的生成?

4) ……..

 

5. 总结

5.1. 重点和难点分析

Day01:

1. MyBatis 是什么,要解决什么问题?

2. MyBatis 底层架构体系?

3. MyBatis 核心组件以及API?

4. MyBatis 编程的基本步骤?

5. MyBatis 数据操作执行流程?

6. ...

 

Day02:

1. MyBatis 框架编程中CRUD操作的基本实现?

1) Session.selectOne(….),session.selectList(…)

2) Session.insert(“命名空间+元素id”),….

3) Session.update(…),….

4) Session.delete(….),…..

2. MyBatis 框架编程中的事务处理(自动提交事务,手动提交事务)?

1) factory.openSession(true),自动提交

2) factory.openSession();默认false,为手动提交

3. MyBatis 框架编程中基于DAO接口的CRUD实现方式?

1) 定义DAO接口

2) 定义Mapper映射文件

3) Mapper文件与DAO接口之间的映射(命名空间,元素id,参数)

4. MyBatis 框架编程中DAO接口与Mapper映射的绑定规则?

1) DAO的类全名与mapper的命名空间相同

2) DAO类的方法名与mapper文件的元素id相同

3) DAO类中的方法参数与mapper文件中参数获取对应

5. MyBatis 框架编程中数据库连接信息的配置及获取方式?

1) UNPOOLED(不使用连接池):了解,每次访问数据库都会创建一个新的连接。

2) POOLED (内置连接池):重点掌握。可以实现Connection对象的复用

3) JNDI(使用命名空间目录接口服务):了解

 

6. MyBatis 框架编程中映射文件内部的对象别名配置?

1) 配置文件中通过typeAliases元素进行配置

2) 应用于映射文件中参数类型(parameterType),结果类型(resultType)

 

Day03.MyBatis增强

业务增强

1. 动态排序

2. ID生成策略

 

高级应用

1. 日志配置

2. 缓存的配置及应用

3. 高级映射的基本实现(many2one,……)

4. 延迟加载的实现

5. 动态SQL的基本应用(foreach,………)

6. ……………

 

思考:mybatis应用过程中你都考虑到了哪些方面的优化?

1) 映射优化(实体类的属性要提供get/set方法,不提供可以映射吗?)

2) 缓存优化(使用缓存时要考虑缓存失效,脏读,对象拷贝,序列化,。。)

3) SQL注入优化($的应用)

4) ID策略优化(自增长)

5) 延迟加载优化(按需加载)

6) 动态SQL优化 (空格问题,..)

7) ……..

 

5.2. 常见FAQ

1. MyBatis 底层数据方法需要JDBC吗?

2. MyBatis 核心配置文件中有哪些常用配置?

3. MyBatis 映射文件中常用元素定义有哪些?

4. MyBatis 数据操作常用实现方式有哪些?

5. MyBatis 映射文件元素如何获取参数数据?

6. MyBatis 映射文件元素中获取参数数据的常用方案.

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值