MyBatis进阶----进阶篇

上篇文章我们说了MyBatis基础-----入门篇,讲解了MyBatis框架入门。CRUD案例以及基础用法等内容。这篇我们继续进行学习,MyBatis进阶----进阶篇。

回忆一下使用MyBatis的步骤:

  1. 在pom.xml中引入相关jar包
  2. 创建全局配置文件,包含数据库参数
  3. 创建实体类,数据库对应表
  4. 编写对应mapper文件
  5. 加载核心配置文件mybatis-config.xml
  6. 创建SqlSessionFactoryBuilder对象,通过.build方法创建会话工厂,读取流
  7. sqlSessionFactory.openSession创建sqlSession对象,执行sql语句。

(5-7步骤:
Resources.getResourceAsStream(“mybatis-config.xml”);加载核心配置文件得到InputStream------>

通过SqlSessionFactoryBuilder------>创建sqlSessionFactoryBuilder对象------>

通过.build()读取InputStream创建sqlsessionFactory会话工厂------->

sqlSessionFactory.openSession();创建sqlSession对象------>调用对应的方法执行sql语句。)

 private SqlSessionFactory sqlSessionFactory;
    @Before
    public void before() throws IOException {
        //加载核心配置文件mybatis-config.xml
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        //创建sqlSessionFactoryBuilder对象
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        //读取配置文件的流,创建sqlSessionFactory会话工厂
        sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
    }

在上一次学习中,我们发现在使用mybatis通过session调用方法进行增删改查时,会因为sql标识符是字符串类型,且参数是object类型,例如在调用select方法时
在这里插入图片描述
这是sql的调用方无法确定sql标识符是否正确,也无法确定应该传递什么样的参数,从而影响了开发效率,易出错。
MyBatis如何解决?引出今天要写的内容

Mapper代理

直接利用session+id来执行sql的方式存在一些问题

  1. session执行sql时都需要提供要执行sql的id,而这个id是字符串类型,意味着id是否正确在编译期间是无法获知的,必须等到运行时才能发现错误,
  2. sql需要的参数和返回值类都不明确,这也增加了出错的概率
    理想的状况:像调用方法一样调用sql,既避免了直接写id的问题,也可以明确指定方法的参数类型和返回值类型

解决方案:

MyBatis通过动态代理来解决, 动态代理的意思就是自动产生一个实现类。
简单的说动态代理(动态生成),就是在运行过程中自动产生一个对象,用它来代理原本已经存在的对象的方法。我们获得的这个mapper就是一个代理对象

MyBatis中本来由Executor(被代理对象)来完成sql的执行,现在由代理对象(自动生成)来代理Executor完成,代理对象会将我们的操作转交给Executor,由mapper代理去与session进行交互,同时屏蔽了容易出错的地方。

问题是:MyBatis怎么知道代理对象是什么样的对象呢?

这就需要为MyBatis提供Mapper接口,这个接口就是对mapper.xml中的sql语句的声明,与DAO层的接口一样,通过接口,我们可以明确的告诉sql的调用者,返回值类型,方法名称,参数分别是什么。

扫描二维码关注公众号,回复: 11034561 查看本文章

MyBatis其实就是获取执行的方法名称作为id来查找sql的,所以方法名和id必须完全一致

使用步骤

  1. 创建接口类
package com.cx.mapper;

import com.cx.pojo.Product;

import java.util.List;
public interface ProductMapper {
    //根据id查询
    Product selectById(int pid);
    //根据name实现模糊查询
    List<Product> selectByName(String pname);
}

  1. 提供响应的sql映射
<?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.cx.mapper.ProductMapper">
    <!--根据id进行查询-->
    <select id="selectById" parameterType="int" resultType="com.cx.pojo.Product">
        select * from product where pid = #{pid}
    </select>
    <!--根据name进行模糊查询-->
    <select id="selectByName" parameterType="String" resultType="com.cx.pojo.Product">
        select * from product where pname like '%${新疆}%'
    </select>
</mapper>
  1. 获取代理对象 执行方法完成操作
import com.cx.mapper.ProductMapper;
import com.cx.pojo.Product;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;


public class MyTest2 {
    private SqlSessionFactory sqlSessionFactory;
    @Before
    public void before() throws IOException {
        //加载核心配置文件mybatis-config.xml
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        //创建sqlSessionFactoryBuilder对象
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        //读取配置文件的流,创建sqlSessionFactory会话工厂
        sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
    }
    @Test
    public void test1(){
        SqlSession sqlSession = sqlSessionFactory.openSession();
        ProductMapper mapper = sqlSession.getMapper(ProductMapper.class);
        Product product = mapper.selectById(4);
        System.out.println(product);
        sqlSession.close();
    }
    @Test
    public void test2(){
        SqlSession sqlSession = sqlSessionFactory.openSession();
        ProductMapper mapper = sqlSession.getMapper(ProductMapper.class);
        List<Product> product = mapper.selectByName("新疆");
        System.out.println(product);
        sqlSession.close();
    }
}

使用mapper代理注意事项

  1. 必须保证mapper.xml中的namespace与接口的全限定名称一致
  2. 方法的名称必须与对应的sql statement的id一致
  3. 方法的参数必须与对应的sql statement的parameterType一致
  4. 方法的返回值必须与对应的sql statement的resultType一致
    案例总结如下:

mapper代理实现步骤:

  1. 创建接口,接口中的方法与sql对应
  2. mapper文件中的namespace指定对应的接口类
  3. 通过sqlsession.getMapper()来获取代理对象

XML配置

MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。 配置文档的顶层结构如下:

>configuration(配置)
  >>properties(属性)
  >>settings(设置)
  >>typeAliases(类型别名)
  >>typeHandlers(类型处理器)
  >>objectFactory(对象工厂)
  >>plugins(插件)
  >>environments(环境配置)
    >>>environment(环境变量)
       >>>transactionManager(事务管理器)
       >>>dataSource(数据源)
  >>databaseIdProvider(数据库厂商标识)
  >>mappers(映射器)

注意:>代表层次结构,我们在书写的时候一档要遵循上面的结构偶进行,否则就会报错。
单独使用MyBatis的场景是很少的,后续都会将其与Spring进行整合,并由Spring来对MyBatis进行配置,下面讲解的为需要重点关注,其余的了解即可,若遇到特殊需求可查阅官方文档

属性(properties)

properties可从配置文件或是properties标签中读取需要的参数,使得配置文件各个部分更加独立

内部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>
        <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql:///sms?serverTimezone=Asia/Shanghai&amp;characterEncoding=utf8"/>
        <property name="username" value="root"/>
        <property name="password" value="***"/>
    </properties>
    <environments default="development">
        <!--        可配置多个环境  并指定默认使用的环境-->
        <environment id="development">
            <!--            指定事务管理器-->
            <transactionManager type="JDBC"/>
            <!--            指定数据源 就是数据来自哪里 这里默认使用MyBatis自带的连接池-->
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <!--                //表示本机 localhost  &amp;就是&  xml中&需要转译-->
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
    <!--    指定要加载的映射文件-->
    <mappers>
        <mapper resource="mapper/ProductMapper.xml"/>
    </mappers>
</configuration>

为了更清晰的表达,标出变化之处:
在这里插入图片描述

外部配置文件

对于我们之前mybatis的mybatis-config.xml的文件,我们直接把jdbc相关参数配置到xml文件中去了,但是实际开发过程中数据量大,就会比较麻烦,我们可以建立一个单独的文件,进行存储,使用属性标签从文件中取值,此时就用到了外部配置文件

jdbc.properties位于resource下
在resource下建立一个jdbc.properties

driver = com.mysql.cj.jdbc.Driver
url = jdbc:mysql:///sms?serverTimezone=Asia/Shanghai&characterEncoding=utf8
username = root
password = ***

mybatis-config.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>
    <!--&lt;!&ndash;内部标签定义的属性&ndash;&gt;
    <properties>
        <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql:///sms?serverTimezone=Asia/Shanghai&amp;characterEncoding=utf8"/>
        <property name="username" value="root"/>
        <property name="password" value="***"/>
    </properties>-->
    <!--外部标签关联属性文件-->
    <properties resource="jdbc.properties" />
    <environments default="development">
        <!--        可配置多个环境  并指定默认使用的环境-->
        <environment id="development">
            <!--            指定事务管理器-->
            <transactionManager type="JDBC"/>
            <!--            指定数据源 就是数据来自哪里 这里默认使用MyBatis自带的连接池-->
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <!--                //表示本机 localhost  &amp;就是&  xml中&需要转译-->
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
    <!--    指定要加载的映射文件-->
    <mappers>
        <mapper resource="mapper/ProductMapper.xml"/>
    </mappers>
</configuration>

在这里插入图片描述
当内部和外部属性出现同名时,则优先使用外部的;

别名(typeAliases)

typeAliases用于为Java的类型取别名,从而简化mapper中类名的书写
比如在mapper.xml中的resultType,要写包名加类名,书写不够简便。
注意typeAliases标签位于properties标签之下,书写顺序不能颠倒

为某个类定义别名

  <!--为某个类定义别名-->
    <typeAliases>
        <!--type时全类名,alias取得时别名,可以省略,省略后默认的是类名,首字母不区分大小写-->
        <typeAlias type="com.cx.pojo.Product" alias="prodcct" />
    </typeAliases>

在这里插入图片描述
那么问题又来了,显示开发中,实体类会很多,这样会比较麻烦,我们可以直接扫描包,批量定义别名:

<!--    批量定义别名-->
    <typeAliases>
        <!--扫描包,都设置为类名,首字母不区分大小写-->
        <package name="com.cx.pojo"/>
    </typeAliases>

上面两种定义别名的方式,用法一样,如下:

<?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.cx.mapper.ProductMapper">
    <!--根据id进行查询-->
    <select id="selectById" parameterType="int" resultType="Product">
        select * from product where pid = #{pid}
    </select>
    <!--根据name进行模糊查询-->
    <select id="selectByName" parameterType="String" resultType="product">
        select * from product where pname like '%${新疆}%'
    </select>
</mapper>

在这里插入图片描述
可以发现,resultType输出映射里面product实体类首字母大小写都可以。

别名冲突

使用package批量设置时很容易出现别名冲突,这是就需要使用@Alias注解来为冲突的类单独设置别名

@Alias("products1")
public class Product {
    private int pid;
    private String pname;
    private float price;
    private Date pdate;
    private String cid;
.....}

注意要扫描的是这个类的包,否则注解是无效的

下面mybatis已经存在的别名:

_byte byte
_long long
_short short
_int int
_integer int
_double double
_float float
_boolean boolean
string String
byte byte
long Long
short Short
int Integer
integer Integer
double Double
float Float
boolean Boolean
date Date
decimal BigDecimal
bigdecimal BigDecimal
object Object
map Map
hashmap HashMap
list List
arraylist ArrayList
collection Collection
iterator Iterator

映射(Mappers)

在mapper.xml文件中定义sql语句后,就必须让MyBatis知道,到哪里去找这些定义好的sql,这就需要在配置文件中指出要加载的mapper的位置;MyBatis支持4种方式来加载mapper文件
以下有四种方式:

  1. 直接指定mapper文件
    resource
    指定资源文件的相对目录 相对于classpath maven项目会自动将java和resources都添加到classpath中
    所以相对与resources来指定路径即可
    <mappers>
        <mapper resource="mapper/ProductMapper.xml"/>
    </mappers>
  1. 指定文件的绝对路径,MyBatis支持但是一般不用
    url
<mappers>
	 <mapper url="file:///Users/jerry/Downloads/MYB/src/main/resources/mapper/ProductsMapper.xml"/>
</mappers>
  1. 通过指定接口 让MyBatis自动去查找对应的Mapper文件
    class
    这种方式要求映射文件和接口处于同一目录下,并且名称相同
    要保证上述要求只需要在resources下创建于接口包名相同的目录即可
    <mappers>
        <mapper resource="<mapper class="com.cx.mapper.ProductMapper"/>
    </mappers>

注意:运行前先clean,否则可能因为之前已经存在target而不会重新编译,导致无法加载新建的mapper文件

4.指定包名称,扫描包下所有接口和映射文件
这种方式同样要求映射文件和接口处于同一目录下,并且名称相同

<mappers>
   <package name="com.cx.mapper"/>
</mappers>

注意:
上面方法3和方法4,

对于普通项目:

我们可以把mapper.xml 和 接口 放在一个目录下

但是对于maven项目:

我们可以把接口文件放在java下,把对应的mapper.xml放在recources下;
1.如果是单级目录,只需保证包名一致:
即在Java和recources下创建同名的文件夹mapper;
2.如果是多级目录此时注意如果要保持目录一致:
即在java我们可以创建时输入 一级目录.二级目录.三级目录 等;
但是在recources下不可以这样创建,应该使用“/” 即:一级目录/二级目录/三级目录 

在这里插入图片描述

此处是因为在recources下会把“.”当成文件名的一部分,创建一个包;
而Java会当成分别包的符号,创建多级目录,虽然看着一样,但是结构不同。
发布了21 篇原创文章 · 获赞 33 · 访问量 667

猜你喜欢

转载自blog.csdn.net/qq_41490938/article/details/105637778