MyBatis

三层架构回顾

 

MyBatis简介

历史层面:

Ø MyBatis的前身是iBatis。

Ø iBatis是Apache在2001年发起的开放源代码项目。

Ø 2010年这个项目由Apache迁移到了Google,并改名为MyBatis。

Ø 2013年迁移到Github。

 

技术层面

Ø 简单易学、功能强大。

Ø MyBatis对JDBC进行了封装,简化了原生JDBC繁杂的操作。

Ø MyBatis是基于ORM实现的持久层框架。

Ø 支持动态SQL:根据不同的查询条件,执行不同的SQL。

Ø SQL语句可以写在xml文件中,和源码分离,提高了可维护性。

 

 

MyBatis帮助文档:

https://mybatis.org/mybatis-3/zh/index.html

 

ORM简介

 

什么是ORM:

 

ORM: Object Relation Mapping,对象关系映射

Object: 实体类、数据库表

Relation:关系

Mapping:映射

 

 

为什么要使用ORM:

在类中的属性名和表中的字段名一致的情况下,可以使用反射技术来统一对JDBC进行操作,简化繁杂的JDBC操作

但是,当属性名和字段名不一致时,就需要使用ORM来指定他们的对应关系,以便反射能够更好的操作JDBC

 

MyBatis快速入门

搭建开发环境

maven环境配置

 

VM options for importer:-Xms1024m -Xmx2048m

VM Potions:-DarchetypeCatalog=local

 

导入xml代码提示模板

 

idea编码设置

 

新建项目

 

新建子模块

 

导入依赖

<dependencies>     <dependency>         <groupId>org.mybatis</groupId>         <artifactId>mybatis</artifactId>         <version>3.5.5</version>     </dependency>     <dependency>         <groupId>mysql</groupId>         <artifactId>mysql-connector-java</artifactId>         <version>5.1.49</version>     </dependency>     <dependency>         <groupId>junit</groupId>         <artifactId>junit</artifactId>         <version>4.12</version>     </dependency> </dependencies>

 

 

新建实体类

建库、建表

 

编码步骤

1、 在resources下创建MyBatis的主配置文件mybatis-config.xml

2、 在主配置文件中配置日志输出和环境(数据源)

 

<?xml version="1.0" encoding="UTF-8"?>

                                

    <!--配置环境(数据源)-->     <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://127.0.0.1:3306/ssm"/>                 <property name="username" value="root"/>                 <property name="password" value="1234"/>             </dataSource>         </environment>     </environments> </configuration>

 

 

3、 配置MyBatis的映射文件

实体类 <===> 数据库表

SQL语句都写在映射文件中

 

<?xml version="1.0" encoding="UTF-8" ?>

 

<mapper namespace="product">

    

    <insert id="insert">         insert into product values(null, #{name}, #{tid}, #{price}, #{num}, #{imgurl}, #{description})     </insert>     <update id="update">         update product set           name=#{name}, tid=#{tid},price=#{price},num=#{num},           imgurl=#{imgurl}, description=#{description}           where id=#{id}     </update>     <delete id="delete">         delete from product where id=#{id}     </delete> </mapper>

 

 

4、 在主配置文件中注册映射文件(注意:如果放在包下,则路径中不是点,而是**/**)

 

<?xml version="1.0" encoding="UTF-8"?>

    ...                        

 

 

5、 编写java代码,使用单元测试

1) 使用MyBatis给我们提供的工具类读取MYBatis的主配置文件

2) 创建SqlSessionFactory,可以理解为创建连接池

3) 获取sqlSession,可以理解为获取连接对象

4) 通过sqlSession对象查询映射文件中的SQL来执行,并获取结果

5) MyBatis对事务默认是手动提交,因此对于增删改操作,需要提交事务

 

public class TestProduct { @Test public void testGet() throws IOException { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); // 链接的工厂对象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); // 获取连接 SqlSession sqlSession = sqlSessionFactory.openSession(); Product product = (Product)sqlSession.selectOne("product.get", 50); System.out.println(product); }

@Test public void testInsert() throws IOException { ​ String resource = "mybatis-config.xml"; ​ InputStream inputStream = Resources.getResourceAsStream(resource); // 链接的工厂对象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); // 获取连接 SqlSession sqlSession = sqlSessionFactory.openSession();

Product product = new Product(); ​ product.setName("测试MyBatis插入操作");

sqlSession.update("product.insert", product); ​ sqlSession.commit(); // 提交事务 } }

 

 

MyBatis结合DAO

方式一:接口和实现类

\1. 编写dao接口

\2. 编写接口的实现类

 

\3. 在Service层调用时跟之前一样

 

方式二:接口和代理类(重点)

 

使用MyBatis创建接口的代理类,并通过接口来调用方法

 

 

MyBatis的工具类抽取

public class MyBatisUtils { private static String resource = "mybatis-config.xml"; private static SqlSessionFactory sqlSessionFactory; // 将连接对象SqlSession和当前线程进行绑定 private static final ThreadLocal<SqlSession> TL = new ThreadLocal<>(); // 初始化 sqlSessionFactory static { try { InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } }

// 链接的工厂对象 public static SqlSession openSession() { // 从线程对象中获取链接 SqlSession sqlSession = TL.get(); if (sqlSession == null) { // 说明当前线程是第一次调用该方法 sqlSession = sqlSessionFactory.openSession(); // 将连接对象和当前线程绑定 TL.set(sqlSession); ​ } return sqlSession; ​ }

public static void commit() { openSession().commit(); ​ }

// 释放资源 public static void release() { // 不是真正的关闭,而是将链接返还给连接池 openSession().close(); TL.remove(); ​ } }

 

MyBatis的简单查询

查询所有

映射文件

 

 

 

resultMap★★★★★

当类中的属性名和表中的字段名不一致时,需要手动映射,如下所示

 

方式1:给字段起别名,如果有很多处查询,维护起来不方便,不可取

 

<select id=**"getAll"** resultType=**"com.bjpowernode.domain.User"> select id,name username,** #给字段起别名 password, nickname, telephone, photo from user </select>

 

 

 

方式2:使用resultMap

resultMap可以映射属性和字段的对应关系

 

<!--     resultMap: 将类中的属性名和表中的字段建立映射关系

    属性名和字段名一致时,可以不指定映射关系,但最好所有字段全部建立映射 --> <resultMap id="userMap" type="com.bjpowernode.domain.User">     <!--         property: 类中的属性名         column:表中的字段名     -->     <id property="id" column="id" />     <result property="username" column="name" />     <result property="password" column="password" />     <result property="nickname" column="nickname" />     <result property="telephone" column="telephone" />     <result property="photo" column="photo" /> </resultMap>

 

 

方法只有一个参数

参数为简单类型时

当参数有且只有一个简单类型时,xml中的#{}中的内容可以随便写,但是最好还是写成参数名(见名知意)!

 

参数为复杂类型时

#{}中直接写对象中的属性名!

 

方法中有多个参数

 

 

 

模糊查询

方式1:(不推荐,原因是业务层写SQL了,不满足低耦合、高内聚)

 

 

方式2(推荐)

 

 

 

方式3:(不推荐,${}是字符串的拼接,存在SQL注入的风险)

 

${}和#{}的区别(面试题)

#{} 是占位符 可以防止SQL注入,会判断值的类型:如果是字符串,两端自动加上引号

${} 字符串的拼接符,存在SQL注入的风险,不会判断值的类型,原样输出

 

获取插入操作时生成的主键

需求:在插入操作之后,获取到生成的ID值。

方式1:

 

 

注意!该方式仅适用于支持主键自增的数据库,像Oracle数据库,它的主键是通过序列来维护的,因此这种方式不适用Oracle数据库

 

方式2:

以下方式适用于所有数据库

 

动态SQL

什么是动态SQL

动态SQL一般是针对查询而言的,所谓动态SQL,就是条件不同,执行的SQL语句不同

 

如何产生动态SQL

在映射文件中,提供了以下标签支持动态SQL:

• if标签:用于判断

• foreach标签:用于循环

 

格式化标签:

• where标签

• set标签(用于修改操作)

 

 

if标签★★★★★★

不固定参数查询(重点)★★★★★

 

还可以通过where标签来完成SQL的格式化

 

不固定参数更新(了解)

 

使用set标签进行格式化,注意,当没有任何满足的条件时,sql中没有set关键字,报SQL语法错误!

 

foreach标签★★★★

通常用于批量操作。

 

使用参数的默认名称

 

 

通过@Param指定参数名称

 

 

 

多表链接查询★★★★★★★★★★★

常见的表关系:

一对多:

订单和用户,一个用户可以下多个订单,但是一个订单只能属于一个

用户和收货地址:一个用户可以添加多个收货地址

商品类型和商品:一个类型,包括多种商品,一个商品属于一个类型

 

多对多:

用户和商品:一个用户可以购买多个商品,一个商品可以被多个用户购买。

用户和角色:一个用户可以拥有多个角色,一个角色可以被多个用户拥有。

角色和权限:一个角色可以拥有多个权限,一个权限可以被多个角色拥有。

 

通常,多对多的关系,都需要第三张表来维护,这个第三张表中会包含两种表中的主键

 

用户表:

id 名称

1 张三

2 李四

 

角色表:

Id 名称

1 普通员工

2 经理

3 组长

 

用户角色表:

Id 用户id 角色id

1 1 1

2 1 2

3 2 1

4 2 2

5 2 3

 

一对一(特殊的一对多关系)

用户和身份证

 

在真是的开发种,一对一的关系不常见,80%以上的情况都是一对多!

 

 

学生表:

 

成绩表

 

需求:查询学生成绩,显示格式:姓名 成绩

 

常见查询方式回顾:

连接查询:

交叉(自然)连接查询:select * from a,b 笛卡尔积,即结果的条数为a表中的记录数乘以b表中的记录数

select a.name,b.score from student a,score b where a.id=b.sid

 

 

 

内连接:select s1.name,s2.score from student [as] s1 [inner] join score s2 on s1.id=s2.sid

特点:取两张表种的交集

 

问题:未参加考试的学生没有出现在结果中!

 

需求:未参加考试的学生,也应该出现在结果中,成绩显示为0或缺考

 

外连接

左外连接:以左表为主,左表中的数据会被全部查询处来,右表中不存在关联数据也会被查询出来,

只不过,没有被关联的数据显示null

 

select s1.name,s2.score from student s1 left join score s2 on s1.id=s2.sid

 

右外连接:以右表为主关联查询,即便左表中没有关联的数据,也会被查询出来,只不过,没有被关联的数据显示null

左外连接的查询,右外连接也可以完成,左外连接比较常用,更符合惯用思维

 

一对多查询(以少查多)

添加类型实体类

 

 

多对一查询(以多查少)

在查询出商品的时候,同时关联查询出对应的类型

 

多表嵌套查询**★★★**

什么叫嵌套查询

嵌套查询(nesting query)又叫做分步**(分多次)**查询。

比如查询所有商品,同时查询商品对应的类别。

连接查询:select p.*,t.id t_id,t.name t_name from product p, type t where p.tid=t.id

商品和类型同时查询出来了

 

嵌套查询:存在n+1条查询语句问题。即商品表中有50条记录(n),sql语句要执行51条。

 

select * from product;// 一次查询,查询出50条 1条

 

如果在查询商品的时候,同时希望把商品的类型也查询出来,则需要再次执行50次SQL语句

select * from type where id=1

select * from type where id=2

...

select * from type where id=50

50次

 

一对多查询(以少查多)

 

 

 

多对一查询(以多查少)

 

 

嵌套查询的加载策略

什么是加载策略

假设Product中有类型对象,在获取该对象的时候,就去查询,不获取就不查询,而不需要单独再写SQL

根据需要决定查还是不查对象中的多的一方或一的一方!

 

延迟加载只能应用于嵌套查询!

全局配置

在mybatis-config.xml中添加如下配置

 

 

 

当没有调用type.getPList()时,没有进行查询类型下的商品列表!

 

 

当调用getpList方法时,执行商品列表的查询操作

 

局部配置

 

如果单独想对某一条查询配置加载策略,则需要在collection或association中通过fetchType属性来指定加载策略

可选值:

eager表示立即加载

lazy 表示延迟加载

 

 

局部配置会覆盖全局配置

 

 

注意

当调用对象中的toString等方法时,也会自动触发懒加载,如果不希望这样做,在配置文件中覆盖配置即可

 

MyBatis的高级配置

引入外部的数据库配置文件

引入jdbc的配置信息

 

使用方式:${配置文件中的key}

 

批量起别名

批量注册映射文件

 

 

 

 

在pom.xml中添加如下配置:

 

对于src/main/java目录下的文件,Maven在进行编译时,只会编译.java文件!

如果希望xml、properties等配置也一起编译,需要添加如下配置

 

<build>     <resources>         <resource>             <directory>src/main/java</directory>             <includes>                 <include>/*.properties</include>                 <include>/*.xml</include>             </includes>             <filtering>false</filtering>         </resource>     </resources> </build>

 

 

 

驼峰命名自动映射(了解)

 

 

查询标签并没有使用resultMap来进行手动映射

 

同样可以查询到结果

MyBatis的缓存(了解)

 

缓存是什么?

将数据从服务器中获取,并保存到本机(磁盘或内存中),保存的这个数据就成为缓存数据。

下次获取的时候,不再从服务器重新获取,而是从本地缓存中取数据,提高效率。

 

一级缓存(内存)

默认开启,并且程序员无法手动干预!

在一次会话(SqlSession)中,当第一次查询出结果之后,会将这个结果保存到内存中,作为缓存数据,当再次进行相同的查询时,直接从缓存中获取数据,提高程序的效率!

 

 

实际上只查询一次数据库

 

二级缓存(磁盘)

开启二级缓存相关的实体类,必须实现序列化接口

 

对于多表(关联、嵌套)查询,都应该避免使用二级缓存,有可能造成脏数据等问题。

 

MyBatis的注解开发(了解!)

使用@Insert@Update@Delete@Select@Results@Result@One@Many等注解,将SQL语句写在java代码中

简单注解的使用

 

手动映射

 

 

嵌套查询映射

 

一对多嵌套:配置文件形式与注解形式的对比

 

 

多对一嵌套

 

结论:简单SQL用注解,复杂SQL用XML

 

 

 

 

 

posted @ 2020-01-07 16:02  Wollf  阅读(150)  评论(0)    收藏  举报