mybatis 概述 | 配置文件详解

一,Mybatis入门和自定义Mybatis

1.框架概述

1)什么是框架

它是我们软件开发中的一套解决方案,不同的框架解决的是不同的问题。

使用框架的好处:
框架封装了很多的细节,使开发者可以使用极简的方式实现功能。大大提高开发效率。

2)三层架构

  • 表现层:
    是用于展示数据的
  • 业务层:
    是处理业务需求
  • 持久层:
    是和数据库交互的

3)持久层技术解决方案

  • JDBC技术:

    Connection
    PreparedStatement
    ResultSet

  • Spring的JdbcTemplate:

    Spring中对jdbc的简单封装

  • Apache的DBUtils:

    它和Spring的JdbcTemplate很像,也是对Jdbc的简单封装

以上这些都不是框架
JDBC是规范
Spring的JdbcTemplate和Apache的DBUtils都只是工具类

4)Mybatis概述

mybatis是一个持久层框架,用java编写的。
它封装了jdbc操作的很多细节,使开发者只需要关注sql语句本身,而无需关注注册驱动,创建连接等繁杂过程它使用了ORM思想实现了结果集的封装。

ORM:
Object Relational Mappging 对象关系映射

简单的说:
就是把数据库表和实体类及实体类的属性对应起来
让我们可以操作实体类就实现操作数据库表。

 user			User
 id				userId
 user_name		userName

我们需要做到
实体类中的属性和数据库表的字段名称保持一致。

 user	     User
 id		     id
 user_name	 user_name
`

2.Mybatis入门

1)mybatis的环境搭建

创建测试数据库;

第一步:创建maven工程并导入坐标

第二步:创建实体类和dao的接口

第三步:创建Mybatis的主配置文件
SqlMapConifg.xml
导入log4j的配置文件

第四步:创建映射配置文件
IUserDao.xml

第五步:编写测试类

2)环境搭建的注意事项

第一个:创建IUserDao.xml 和 IUserDao.java时名称是为了和我们之前的知识保持一致。
在Mybatis中它把持久层的操作接口名称和映射文件也叫做:Mapper
所以:IUserDao 和 IUserMapper是一样的

第二个:在idea中创建目录的时候,它和包是不一样的
包在创建时:com.itheima.dao它是三级结构
目录在创建时:com.itheima.dao是一级目录

第三个:mybatis的映射配置文件位置必须和dao接口的包结构相同

第四个:映射配置文件的mapper标签namespace属性的取值必须是dao接口的全限定类名

第五个:映射配置文件的操作配置(select),id属性的取值必须是dao接口的方法名

当我们遵从了第三,四,五点之后,我们在开发中就无须再写dao的实现类。

3)mybatis的入门案例

第一步:读取配置文件
第二步:创建SqlSessionFactory工厂
第三步:创建SqlSession
第四步:创建Dao接口的代理对象
第五步:执行dao中的方法
第六步:释放资源

注意事项:
不要忘记在映射配置中告知mybatis要封装到哪个实体类中
配置的方式:指定实体类的全限定类名

4)基于xml形式的配置

pom.xml

<dependencies>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.4.5</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.32</version>
    </dependency>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
</dependencies>

User

public class User {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    public Integer getId() {
        return id;
    }

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

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", birthday=" + birthday +
                ", sex='" + sex + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

UserDao

public interface UserDao {
    List<User> findAll();
}

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>
    <!-- 配置 mybatis 的环境 -->
    <environments default="mysql">
        <!-- 配置 mysql 的环境 -->
        <environment id="mysql">
            <!-- 配置事务的类型 -->
            <transactionManager type="JDBC"></transactionManager>
            <!-- 配置连接数据库的信息:用的是数据源(连接池) -->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/eesy"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <!-- 告知 mybatis 映射配置的位置 -->
    <mappers>
        <mapper resource="com/atguigu/dao/UserDao.xml"/>
    </mappers>
</configuration>

userDao.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.atguigu.dao.UserDao">
    <select id="findAll" resultType="com.atguigu.domain.User">
        select * from user;
    </select>
</mapper>

测试类

public class Test1 {
    @Test
    public void test1()throws Exception{
        //1.加载配置文件
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        //2.创造构建者对象
        SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
        //3.构建者构件工厂
        SqlSessionFactory factory = builder.build(is);
        //4.使用工厂生产sqlSession
        SqlSession session = factory.openSession();
        //5.使用sqlSession创建userDao的代理对象
        UserDao dao = session.getMapper(UserDao.class);
        List<User> users = dao.findAll();
        for (User user:users){
            System.out.println(user);
        }
        //6.资源关闭
        session.close();
        is.close();
    }
}

5)基于注解形式配置

mybatis基于注解的入门案例:
把IUserDao.xml移除,在dao接口的方法上使用@Select注解,并且指定SQL语句
同时需要在SqlMapConfig.xml中的mapper配置时,使用class属性指定dao接口的全限定类名。

Dao层

@select("select * from user")
public interface UserDao {
    List<User> findAll();
}

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>
    <!-- 配置 mybatis 的环境 -->
    <environments default="mysql">
        <!-- 配置 mysql 的环境 -->
        <environment id="mysql">
            <!-- 配置事务的类型 -->
            <transactionManager type="JDBC"></transactionManager>
            <!-- 配置连接数据库的信息:用的是数据源(连接池) -->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/eesy"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
    <!-- 告知 mybatis 映射配置的位置 -->
    <mappers>
        <mapper resource="com.atguigu.dao.UserDao"/>
    </mappers>
</configuration>

6)带有实现类的dao

明确:
我们在实际开发中,都是越简便越好,所以都是采用不写dao实现类的方式。
不管使用XML还是注解配置。
但是Mybatis它是支持写dao实现类的。

UserDaoImpl

/**
 * @author Guohai
 * @createTime 2020-07-14 20:40
 */
public class UserDaoImpl implements UserDao {
    private SqlSessionFactory factory;

    public UserDaoImpl(SqlSessionFactory factory) {
        this.factory = factory;
    }

    public List<User> findAll() {
        SqlSession session = factory.openSession();
        List<User> list = session.selectList("com.atguigu.dao.impl.UserDaoImpl");
        session.close();
        return list;
    }
}

测试类

public void test1()throws Exception{
    //1.加载配置文件
    InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
    //2.创造构建者对象
    SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
    //3.构建者构件工厂
    /**
     * 创建工厂,Mybatis使用了构建者模式,
     * 把对象的创建细节隐藏,使使用者直接调用方法
     * 就可以拿到对象。
     */
    SqlSessionFactory factory = builder.build(is);
    //4.使用工厂生产sqlSession
    /**
     * 使用工厂模式生产SQLSession对象,
     * 优势:解耦,降低了类之间的依赖关系
     */
    SqlSession session = factory.openSession();
    //5.使用sqlSession创建userDao的代理对象
    /**
     * 创建dao接口实现类使用了代理模式,
     * 在不改变源码的基础上对方法进行增强。
     */
    UserDao dao = session.getMapper(UserDao.class);
    List<User> users = dao.findAll();
    for (User user:users){
        System.out.println(user);
    }
    //6.资源关闭
    session.close();
    is.close();
}

关于配置文件加载的详解

读取配置文件,用到的就是解析xml的技术,
此处用到dom4j解析xml技术。
mybatis-config.xml
1.提供数据源(提供连接对象)
2.指定映射配置信息(指定xxxDao.xml)

xxxDao.xml

1.查询语句
2.封装结果集对象的全限定类名
namespace:xxxDao
-将查询语句和封装结果集对象的全限定类名
封装在一个map集合中,方便查询,
key为dao接口名.方法名,
value为:查询语句和封装结果集对象的全限定类名。

创建代理对象分析

public static void main(String[] args) {
    UserDao userDao;
    /**
     * @param 1.被代理类的类加载器
     * @param 2.被代理类实现的接口
     * @param 3.对方法的增强:listAll()就是在此处被调用的。
     */
    UserDao daoimpl = (UserDao) Proxy.newProxyInstance(
        	UserDao.class.getClassLoader(),
            UserDao.class.getInterfaces(),
            new InvocationHandler() {
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    return null;//在此处调用查询所有的方法
                }
            });
}

7)Mybatis的执行流程

在这里插入图片描述

3.自定义Mybatis分析

构建一个属于自己的持久层框架,将会涉及到的一些知识点:工厂模式
(Factory 工厂模式、构造者模式(Builder 模式、代理模式,反射,自定义注解,注解的反射,xml 解析,
数据库元数据,元数据的反射等)

1.用一个流来加载配置文件
	解析xml文件
2.用一个类来创建工厂类
	构建者模式,隐藏创建细节
3.用工厂类来造sqlSession对象
	工厂模式造对象,解耦
4.使用SQLSession动态代理创建dao的接口实现类对象
	不改变源码的基础上,对原有方法进行增强。

二,基于代理Dao实现CRUD

1.环境搭建

第一步:创建 maven 工程
第二步:导入坐标
第三步:编写必要代码(实体类和持久层接口)
第四步:编写 mybatis-config.xml
第五步:编写映射配置文件
第六步:编写测试类

2.使用要求

1、持久层接口和持久层接口的映射配置必须在相同的包下。
2、持久层映射配置中 mapper 标签的 namespace 属性取值必须是持久层接口的全限定类名。
3、SQL 语句的配置标签,,,的 id 属性必须和持久层接口的方法名相同。

3.实现功能

1.测试查询所有的方法
2.添加方法
3.删除
4.更新
5.根据id查询
6.模糊查询
7.使用聚合函数查询:查询表中有多少条记录数
8.parameterType传递pojo类型参数:根据名字查询

代码实现

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>
    <!-- 配置 mybatis 的环境 -->
    <environments default="mysql">
        <!-- 配置 mysql 的环境 -->
        <environment id="mysql">
            <!-- 配置事务的类型 -->
            <transactionManager type="JDBC"></transactionManager>
            <!-- 配置连接数据库的信息:用的是数据源(连接池) -->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/eesy"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <!-- 告知 mybatis 映射配置的位置 -->
    <mappers>
        <mapper resource="com/atguigu/dao/UserDao.xml"/>
    </mappers>
</configuration>

UserDao.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.atguigu.dao.UserDao">
    <!--自定义封装结果集-->
    <!-- 建立 User 实体和数据库表的对应关系
         type 属性:指定实体类的全限定类名
         id 属性:给定一个唯一标识,是给查询 select 标签引用用的。
    -->
    <resultMap id="map" type="com.atguigu.domain.User">
        <!--配置主键-->
        <id column="id" property="id"></id>
        <!--配置其他列-->
        <result column="birthday" property="birthday"></result>
        <result column="sex" property="sex"></result>
        <result column="address" property="address"></result>
        <result column="username" property="username"></result>
        <!--
            id 标签:用于指定主键字段
            result 标签:用于指定非主键字段
            column 属性:用于指定数据库列名
            property 属性:用于指定实体类属性名称
        -->
    </resultMap>
  
    <!--查询所有-->
    <select id="findAll" resultType="com.atguigu.domain.User">
        select * from user;
    </select>
  
    <!--添加方法-->
    <insert id="saveUser" parameterType="com.atguigu.domain.User">
        <!-- 配置保存时获取插入的 id -->
        <selectKey keyColumn="id" keyProperty="id" resultType="int" order="AFTER">
            select last_insert_id();
        </selectKey>
            insert into user(username, birthday, sex, address)
            values (#{username}, #{birthday}, #{sex}, #{address});
    </insert>
  
    <!--删除-->
    <delete id="deleteUser" parameterType="java.lang.Integer">
      	<!--
          #{id}:作为占位符,名字可以随便起。
          parameterType:只要能表示出是int就可以
   		-->
        delete from user where id = #{id};
    </delete>
  
    <!--更新-->
    <update id="updateUser" parameterType="com.atguigu.domain.User">
        update user
        set username=#{username},
            birthday=#{birthday},
            sex=#{sex},
            address=#{address}
        where id = #{id};
    </update>
  
    <!--根据id查询-->
    <select id="findById" parameterType="int" resultType="com.atguigu.domain.User">
        select * from user where id = #{id};
    </select>
  
    <!--模糊查询-->
    <select id="findLike" resultType="com.atguigu.domain.User" parameterType="String">
        <!--此种写法,需要在调用方法传递的参数两边加%-->
        <!-- select * from user where username like #{name};-->
        <!--
            我们在上面将原来的#{}占位符,改成了${value}。
             注意如果用模糊查询的这种写法,那么${value}的写法就是固定的,不能写成其它名字。
         -->
        select * from user where username like '%${value}%';
    </select>
  
    <!--聚合函数-->
    <select id="count" resultType="java.lang.Integer">
        select count(*) from user;
    </select>
  
    <!--parameterType传递pojo类型参数,根据名字查询-->
    <select id="find2" resultType="com.atguigu.domain.User"
            parameterType="com.atguigu.domain.MyQuery">
        select * from user where username like #{user.username};
    </select>
</mapper>

UserDao

public interface UserDao {
    /**
     * 查询所有
     */
    List<User> findAll();
    /**
     * 添加
     */
    void saveUser(User user);
    /**
     * 删除
     */
    void deleteUser(Integer id);
    /**
     * 更新
     */
    void updateUser(User user);
    /**
     * 根据id查询
     */
    User findById(Integer id);
    /**
     * 模糊查询
     */
    List<User> findLike(String name);
    /**
     * 使用聚合函数查询
     * 查询表中有多少条记录数
     */
    Integer count();
    /**
     * parameterType传递pojo类型参数
     * 根据名字查询
     */
    List<User> find2(MyQuery query);
}

测试类

public class Test1 {

    private InputStream is;
    private SqlSession session;
    private UserDao userDao;
  
    @Before
    public void init() throws Exception {
        is = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
        session = factory.openSession();
        userDao = session.getMapper(UserDao.class);
    }
  
    @After
    public void destory()throws Exception{
        session.commit();//默认自动提交为false
        session.close();
        is.close();
    }
    /**
     * 测试查询所有的方法
     */
    @Test
    public void test1(){
        List<User> users = userDao.findAll();
        for (User user:users){
            System.out.println(user);
        }
    }
    /**
     * 添加方法
     */
    @Test
    public void test2(){
        User user = new User();
        user.setUsername("张三");
        user.setSex("男");
        user.setAddress("黑龙江");
        user.setBirthday(new Date());
        userDao.saveUser(user);
        //保存后返回用户id
        System.out.println(user.getId());
    }
    /**
     * 删除
     */
    @Test
    public void test3(){
        userDao.deleteUser(41);
    }
    /**
     * 更新
     */
    @Test
    public void test4(){
        User user = new User();
        user.setUsername("李四");
        user.setSex("男");
        user.setAddress("黑龙江");
        user.setBirthday(new Date());
        user.setId(49);
        userDao.updateUser(user);
    }
    /**
     * 根据id查询
     */
    @Test
    public void test5(){
        User user = userDao.findById(49);
        System.out.println(user);

    }
    /**
     * 模糊查询1
     * 第一种配置方式#{username}
     */
    @Test
    public void test6(){
        List<User> users = userDao.findLike("%王%");
        for (User user :users){
            System.out.println(user);
        }
    }
    /**
     * 模糊查询2
     * 第二种配置方式:${value}固定写法
     */
    @Test
    public void test7(){
        List<User> users = userDao.findLike("王");
        for (User user :users){
            System.out.println(user);
        }
    }
    /**
     * 使用聚合函数查询
     * 查询表中有多少条记录数
     */
    @Test
    public void test8(){
        Integer count = userDao.count();
        System.out.println(count);
    }
    /**
     * parameterType传递pojo类型参数
     * 根据名字查询
     */
    @Test
    public void test9(){
        MyQuery myQuery = new MyQuery();
        User user = new User();
        user.setUsername("%王%");
        myQuery.setUser(user);
        List<User> users = userDao.find2(myQuery);
        for (User u :users){
            System.out.println(u);
        }
    }
}

4.Mybatis-config.xml

1)全局配置文件结构

configuration 配置
  -properties(属性)
      --property
  -settings(全局配置参数)
      --setting
  -typeAliases(类型别名)
      --typeAliase
      --package
  -typeHandlers(类型处理器)
  -objectFactory(对象工厂)
  -plugins(插件)
  -environments(环境集合属性对象)
      --environment(环境子属性对象)
          ---transactionManager(事务管理)
          ---dataSource(数据源)
  -databaseIdProvider (数据库厂商标识)
  -mappers(映射器)
      --mapper
      --package

2)properties(属性)

在使用 properties 标签配置时,我们可以采用两种方式指定属性配置。

①第一种,配置放在本配置文件内

<properties> 
	<property name="jdbc.driver" value="com.mysql.jdbc.Driver"/>
	<property name="jdbc.url" value="jdbc:mysql://localhost:3306/eesy"/>
	<property name="jdbc.username" value="root"/>
	<property name="jdbc.password" value="root"/>
</properties>

②第二种,配置放在单独的配置文件

在 classpath 下定义 db.properties文件

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/eesy
jdbc.username=root
jdbc.password=123456

properties标签配置

<!-- 配置连接数据库的信息
	resource 属性:用于指定 properties 配置文件的位置,要求配置文件必须在类路径下
	resource="jdbcConfig.properties"
	url 属性:
	URL: Uniform Resource Locator 统一资源定位符
	http://localhost:8080/mystroe/CategoryServlet URL
					协议 主机 端口 URI
	URI:Uniform Resource Identifier 统一资源标识符
		/mystroe/CategoryServlet
	它是可以在 web 应用中唯一定位一个资源的路径
--> 
<properties resource="db.properties"></properties>

此时dataSource 标签就变成了引用上面的配置(动态设置)

<dataSource type="POOLED"> 
    <property name="driver" value="${jdbc.driver}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</dataSource>

3)settings(设置)

这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。

包含如下的 setting 设置:
<settings>
  <setting name="cacheEnabled" value="true"/>
  <setting name="lazyLoadingEnabled" value="true"/>
  <setting name="multipleResultSetsEnabled" value="true"/>
  <setting name="useColumnLabel" value="true"/>
  <setting name="useGeneratedKeys" value="false"/>
  <setting name="autoMappingBehavior" value="PARTIAL"/>
  <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
  <setting name="defaultExecutorType" value="SIMPLE"/>
  <setting name="defaultStatementTimeout" value="25"/>
  <setting name="defaultFetchSize" value="100"/>
  <setting name="safeRowBoundsEnabled"value="false"/>
  <setting name="mapUnderscoreToCamelCase" value="false"/>
  <setting name="localCacheScope" value="SESSION"/>
  <setting name="jdbcTypeForNull" value="OTHER"/>
  <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>

4)typeAliases(类型别名)

在前面我们讲的 Mybatis 支持的默认别名,我们也可以采用自定义别名方式来开发。

第一种,类型别名是为 Java 类型设置一个短的名字,可以方便我们引用某个类。

<typeAliases>
    <typeAlias alias="user" type="com.atguigu.domain.User"/>
</typeAliases>

第二种,类很多的情况下,可以批量设置别名这个包下的每一个类创建一个默认的别名,就是简单类名小写。

<typeAliases>
	<package name="com.atguigu.domain.beans"/>
</typeAliases>

5)typeHandlers(类型处理器)

  • 无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。

  • 日期和时间的处理,JDK1.8 以前一直是个头疼的问题。我们通常使用 JSR310 规范领导者 Stephen Colebourne 创建的 Joda-Time 来操作。1.8 已经实现全部的 JSR310 规范了

  • 日期时间处理上,我们可以使用 MyBatis 基于 JSR310(Date and Time API)编写的各种日期时间类型处理器。

  • MyBatis3.4 以前的版本需要我们手动注册这些处理器,以后的版本都是自动注册的,如需注册,需要下载 mybatistypehandlers-jsr310,并通过如下方式注册。

自定义类型转换器
①我们可以重写类型处理器或创建自己的类型处理器来处理不支持的或非标准的类型。

②步骤:
实现 org.apache.ibatis.type.TypeHandler 接口或者继承org.apache.ibatis.type.BaseTypeHandler
指定其映射某个 JDBC 类型(可选操作)
在mybatis全局配置文件中注册

6)environments 环境配置

environments标签中的default属性指定一个环境的标识符来快速的切换环境。

每种环境使用一个 environment 标签进行配置并指定唯一标识符。

<!--
environment-指定具体环境
id:指定当前环境的唯一标识,transactionManager、和 dataSource 都必须有。
 
*	transactionManager:
type: JDBC | MANAGED | 自定义
	JDBC:使用了 JDBC 的提交和回滚设置,依赖于从数据源得到的连接来管理事务范围JdbcTransactionFactory
	MANAGED:不提交或回滚一个连接、让容器来管理事务的整个生命周期(比如 JEE应用服务器的上下文)。 ManagedTransactionFactory
	自定义:实现 TransactionFactory 接口,type=全类名/别名

*	dataSource
type: UNPOOLED | POOLED | JNDI | 自定义
    UNPOOLED:不使用连接池, UnpooledDataSourceFactory
    POOLED:使用连接池, PooledDataSourceFactory
    JNDI: 在 EJB 或应用服务器这类容器中查找指定的数据源
    自定义:实现 DataSourceFactory 接口,定义数据源的获取方式。

实际开发中我们使用 Spring 管理数据源,并进行事务控制的配置来覆盖上述配置。
-->
<environments default="oracle">
  <environment id="mysql">
  	<transactionManager type="JDBC"/>
 	<dataSource type="POOLED">
  		<property name="driver" value="${jdbc.driver}"/>
  		<property name="url" value="${jdbc.url}"/>
  		<property name="username" value="${jdbc.username}"/>
  		<property name="password" value="${jdbc.password}"/>
    </dataSource>
  </environment>
<environments>

7)mappers(映射器)

作用:用来在 mybatis 初始化的时候,告诉 mybatis 需要引入哪些 Mapper 映射文件。
mapper 属性:

  • resource : 引入类路径下的文件

  • url : 引入网络路径或者是磁盘路径下的文件

  • class : 引入 Mapper 接口

    有 SQL 映射文件 , 要求 Mapper 接口与 SQL 映射文件同名同。

    没有 SQL 映射文件 , 使用注解在接口的方法上写 SQL 语句。

使用相对于类路径的资源
如:<mapper resource="com/itheima/dao/IUserDao.xml" />
使用 mapper 接口类路径
如:<mapper class="com.itheima.dao.UserDao"/>
注意:此种方法要求 mapper 接口名称和 mapper 映射文件名称相同,且放在同一个目录中。
注册指定包下的所有 mapper 接口
如:<package name="cn.itcast.mybatis.mapper"/>
注意:此种方法要求 mapper 接口名称和 mapper 映射文件名称相同,且放在同一个目录中。

5.XxxDao.xml映射文件

1)select 标签

resultType 属性:
	用于指定结果集的类型。
parameterType 属性:
	用于指定传入参数的类型。
sql 语句中使用#{}字符: 它代表占位符,相当于原来 jdbc 部分所学的?,都是用于执行语句时替换实际的数据。
具体的数据是由#{}里面的内容决定的。
#{}中内容的写法:
	由于数据类型是基本类型,所以此处可以随意写。

例如:
<select id="getEmployeeById"
		resultType="com.atguigu.mybatis.beans.Employee"
		databaseId="mysql">
	select * from tbl_employee where id = ${_parameter}
</select>

2)insert标签

parameterType 属性:
	代表参数的类型,因为我们要传入的是一个类的对象,所以类型就写类的全名称。
sql 语句中使用#{}字符: 它代表占位符,相当于原来 jdbc 部分所学的?,都是用于执行语句时替换实际的数据。
	具体的数据是由#{}里面的内容决定的。
#{}中内容的写法:
	由于我们保存方法的参数是 一个 User 对象,此处要写 User 对象中的属性名称。
它用的是 ognl 表达式。

例如:
<insert id="insertEmployee"
		parameterType="com.atguigu.mybatis.beans.Employee"
		databaseId="mysql">
	insert into tbl_employee(last_name,email,gender)
	values(#{lastName},#{email},#{gender})
</insert>

3)OGNL表达式

ognl 表达式:
	它是 apache 提供的一种表达式语言,全称是:Object Graphic Navigation Language 对象图导航语言,它是按照一定的语法格式来获取数据的。
	
语法格式:
	使用 #{对象.对象}的方式。
说明:	
#{user.username}它会先去找 user 对象,然后在 user 对象中找到 username 属性,并调用
getUsername()方法把值取出来。但是我们在 parameterType 属性上指定了实体类名称,所以可以省略 user.
而直接写 username。

4)主键生成方式、获取主键值

①主键生成方式

  1. 支持主键自增,例如 MySQL 数据库
  2. 不支持主键自增,例如 Oracle 数据库

②获取主键值

若数据库支持自动生成主键的字段(比如 MySQL 和 SQL Server),则可以设置useGeneratedKeys=“true”,然后再把keyProperty设置到目标属性上。

<insert id="insertEmployee"parameterType="com.atguigu.beans.Employee"
		databaseId="mysql"
		useGeneratedKeys="true"
		keyProperty="id">
  insert into tbl_employee(last_name,email,gender)
  values(#{lastName},#{email},#{gender})
</insert>

而对于不支持自增型主键的数据库(例如 Oracle),则可以使用 selectKey 子元素:selectKey 元素将会首先运行,id 会被设置,然后插入语句会被调用。

<insert id="insertEmployee" parameterType="com.atguigu.beans.Employee" databaseId="Oracle">
  <selectKey order="BEFORE" keyProperty="id" resultType="integer">
	 select employee_seq.nextval from dual
  </selectKey>
	 insert into orcl_employee(id,last_name,email,gender)
  	 value(#{id},#{lastName},#{email},#{gender})
</insert>

4)新增用户id的返回

新增用户后,同时还要返回当前新增用户的 id 值,因为 id 是由数据库的自动增长来实现的,所以就相
当于我们要在新增后将自动增长 auto_increment 的值返回。
<insert id="saveUser" parameterType="USER">
	<!-- 配置保存时获取插入的 id -->
	<selectKey keyColumn="id" keyProperty="id" resultType="int">
		select last_insert_id();
	</selectKey>
	insert into user(username,birthday,sex,address) 
	values(#{username},#{birthday},#{sex},#{address})
</insert>

5)根据名称模糊查询

①第一种方式

我们在配置文件中没有加入%来作为模糊查询的条件,所以在传入字符串实参时,就需要给定模糊查询的标识%。配置文件中的#{username}也只是一个占位符,所以 SQL 语句显示为“ ?”。

<!-- 根据名称模糊查询 --> 
<select id="findByName" resultType="com.itheima.domain.User" parameterType="String">
 	select * from user where username like #{username}
</select>
@Test
 public void testFindByName(){
 	//5.执行查询一个方法
 	List<User> users = userDao.findByName("%王%");
 	for(User user : users){
 		System.out.println(user);
 	}
 }

②第二种方式

<!-- 根据名称模糊查询 --> 
<select id="findByName" parameterType="string" 	resultType="com.itheima.domain.User">
 	select * from user where username like '%${value}%'
</select>
我们在上面将原来的#{}占位符,改成了${value}。注意如果用模糊查询的这种写法,那么${value}的写法就是固定的,不能写成其它名字。
/**
 * 测试模糊查询操作
 */
@Test
public void testFindByName(){
   //5.执行查询一个方法
  List<User> users = userDao.findByName("王");
  for(User user : users){
    System.out.println(user);
  } 
}

6)#{}和${}的区别

#{}表示一个占位符号
	通过#{}可以实现 preparedStatement 向占位符中设置值,自动进行 java 类型和 jdbc 类型转换,#{}可以有效防止 sql 注入。 #{}可以接收简单类型值或 pojo 属性值。 如果 parameterType 传输单个简单类型值,#{}括号中可以是 value 或其它名称。
	
${}表示拼接sql串
	通过${}可以将 parameterType 传入的内容拼接在 sql 中且不进行 jdbc 类型转换, ${}可以接收简单类型值或 pojo 属性值,如果 parameterType 传输单个简单类型值,${}括号中只能是 value。${} 有 SQL 注入问题。

7)模糊查询${value}的源码

@Override
public String handleToken(String content) {
  Object parameter = context.getBindings().get("_parameter");
  if (parameter == null) {
    context.getBindings().put("value", null);
  } else if (SimpleTypeRegistry.isSimpleType(parameter.getClass())) {
    context.getBindings().put("value", parameter);
  }
  Object value = OgnlCache.getValue(content, context.getBindings());
  String srtValue = (value == null ? "" : String.valueOf(value)); 
  checkInjection(srtValue);
  return srtValue;
}

这就说明了源码中指定了读取的 key 的名字就是”value”,所以我们在绑定参数时就只能叫 value 的名字了。

8)parameterType 传递实体类的包装类

开发中通过 pojo 传递查询条件 ,查询条件是综合的查询条件,不仅包括用户查询条件还包括其它的查询条件(比如将用户购买商品信息也作为查询条件),这时可以使用包装对象传递输入参数。

Pojo 类中包含 pojo。

需求:根据用户名查询用户信息,查询条件放到 QueryVo 的 user 属性中。

MyQuery类

public class MyQuery {
    private User user;
    private Integer password;

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public Integer getPassword() {
        return password;
    }

    public void setPassword(Integer password) {
        this.password = password;
    }
}

配置文件

<!--parameterType传递pojo类型参数,根据名字查询-->
<select id="find2" resultType="com.atguigu.domain.User"
        parameterType="com.atguigu.domain.MyQuery">
    select * from user where username like #{user.username};
</select>

测试类

/**
 * parameterType传递pojo类型参数
 * 根据名字查询
 */
@Test
public void test9(){
    MyQuery myQuery = new MyQuery();
    User user = new User();
    user.setUsername("%王%");
    myQuery.setUser(user);
    List<User> users = userDao.find2(myQuery);
    for (User u :users){
        System.out.println(u);
    }
}

9)resultType 配置结果类型

封装结果集
resultType配置结果类型
当他为实体类全限定类名,必须让实体类的属性名与数据库表的列名对应,否则,数据会封装不进去,当然,也存在解决办法。

解决办法:
1.起别名,在sql语句中给数据库表的列名起别名,别名与实体类的属性名一致。
优点:执行效率高
缺点:开发效率低

2.配置resultMap,自定义一个resultMap,在select标签中进行引用。
优点:开发效率高
缺点:执行效率低

<resultMap id="userMap" type="com.atguigu.domain.User">
    <!--配置主键-->
    <id column="id" property="id"></id>
    <!--配置其他列-->
    <result column="birthday" property="birthday"></result>
    <result column="sex" property="sex"></result>
    <result column="address" property="address"></result>
    <result column="username" property="username"></result>
    <!--
        id 标签:用于指定主键字段
        result 标签:用于指定非主键字段
        column 属性:用于指定数据库列名
        property 属性:用于指定实体类属性名称
    -->
</resultMap>
<!-- 配置查询所有操作 --> 
<select id="findAll" resultMap="userMap">
	select * from user
</select>

6.Mybatis与jdbc编程比较

1.数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可解决此问题。
解决:在 SqlMapConfig.xml 中配置数据链接池,使用连接池管理数据库链接。

2.Sql 语句写在代码中造成代码不易维护,实际应用 sql 变化的可能较大,sql 变动需要改变 java 代码。
解决:将 Sql 语句配置在 XXXXmapper.xml 文件中与 java 代码分离。

3.向 sql 语句传参数麻烦,因为 sql 语句的 where 条件不一定,可能多也可能少,占位符需要和参数对应。
解决:Mybatis 自动将 java 对象映射至 sql 语句,通过 statement 中的 parameterType 定义输入参数的类型。

4.对结果集解析麻烦,sql 变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成 pojo 对象解析比较方便。
解决:Mybatis 自动将 sql 执行结果映射至 java 对象,通过 statement 中的 resultType 定义输出结果的类型

三, Mybatis 传统 DAO 层开发[了解]

Dao接口

public interface UserDao {
    /**
     * 查询所有
     */
    List<User> findAll();
    /**
     * 添加
     */
    void saveUser(User user);
    /**
     * 删除
     */
    void deleteUser(Integer id);
    /**
     * 更新
     */
    void updateUser(User user);
    /**
     * 根据id查询
     */
    User findById(Integer id);
    /**
     * 模糊查询
     */
    List<User> findLike(String name);
    /**
     * 使用聚合函数查询
     * 查询表中有多少条记录数
     */
    Integer count();
}

Dao接口实现类

public class UserDaoImpl implements UserDao {

    private SqlSessionFactory factory;

    public UserDaoImpl(SqlSessionFactory factory) {
        this.factory = factory;
    }

    public List<User> findAll() {
        SqlSession session = factory.openSession();
        List<User> users = session.selectList("com.atguigu.dao.UserDao.findAll");
        session.close();
        return users;
    }

    public void saveUser(User user) {
        SqlSession session = factory.openSession();
        session.insert("com.atguigu.dao.UserDao.saveUser",user);
        session.commit();
        session.close();
    }

    public void deleteUser(Integer id) {
        SqlSession session = factory.openSession();
        session.delete("com.atguigu.dao.UserDao.deleteUser",id);
        session.commit();
        session.close();
    }

    public void updateUser(User user) {
        SqlSession session = factory.openSession();
        session.update("com.atguigu.dao.UserDao.updateUser",user);
        session.commit();
        session.close();
    }

    public User findById(Integer id) {
        SqlSession session = factory.openSession();
        User user = session.selectOne("com.atguigu.dao.UserDao.findById", id);
        session.commit();
        session.close();
        return user;
    }

    public List<User> findLike(String name) {
        SqlSession session = factory.openSession();
        List<User> users = session.selectList("com.atguigu.dao.UserDao.findAll",name);
        session.close();
        return users;
    }

    public Integer count() {
        SqlSession session = factory.openSession();
        Integer o = session.selectOne("com.atguigu.dao.UserDao.count");
        session.commit();
        session.close();
        return o;
    }

}

配置文件

<?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.atguigu.dao.UserDao">
    <!--自定义封装结果集-->
    <!-- 建立 User 实体和数据库表的对应关系
         type 属性:指定实体类的全限定类名
         id 属性:给定一个唯一标识,是给查询 select 标签引用用的。
    -->
    <resultMap id="map" type="com.atguigu.domain.User">
        <!--配置主键-->
        <id column="id" property="id"></id>
        <!--配置其他列-->
        <result column="birthday" property="birthday"></result>
        <result column="sex" property="sex"></result>
        <result column="address" property="address"></result>
        <result column="username" property="username"></result>
        <!--
            id 标签:用于指定主键字段
            result 标签:用于指定非主键字段
            column 属性:用于指定数据库列名
            property 属性:用于指定实体类属性名称
        -->
    </resultMap>
    <!--查询所有-->
    <select id="findAll" resultType="com.atguigu.domain.User">
        select *
        from user;
    </select>
    <!--添加方法-->
    <insert id="saveUser" parameterType="com.atguigu.domain.User">
        <!-- 配置保存时获取插入的 id -->
    <selectKey keyColumn="id" keyProperty="id" resultType="int" order="AFTER">
        select last_insert_id();
    </selectKey>
        insert into user(username, birthday, sex, address)
        values (#{username}, #{birthday}, #{sex}, #{address});
    </insert>
    <!--删除-->
    <!--
        #{id}:作为占位符,名字可以随便起。
        parameterType:只要能表示出是int就可以
    -->
    <delete id="deleteUser" parameterType="java.lang.Integer">

        delete
        from user
        where id = #{id};
    </delete>
    <!--更新-->
    <update id="updateUser" parameterType="com.atguigu.domain.User">
        update user
        set username=#{username},
            birthday=#{birthday},
            sex=#{sex},
            address=#{address}
        where id = #{id};
    </update>
    <!--根据id查询-->
    <select id="findById" parameterType="int" resultType="com.atguigu.domain.User">
        select *
        from user
        where id = #{id};
    </select>
    <!--模糊查询-->
    <select id="findLike" resultType="com.atguigu.domain.User" parameterType="String">
        <!--此种写法,需要在调用方法传递的参数两边加%-->
        <!-- select * from user where username like #{name};-->
        <!--
            我们在上面将原来的#{}占位符,改成了${value}。
             注意如果用模糊查询的这种写法,那么${value}的写
             法就是固定的,不能写成其它名字。
         -->
        select * from user where username like '%${value}%';
    </select>
    <!--聚合函数-->
    <select id="count" resultType="java.lang.Integer">
        select count(*)
        from user;
    </select>
    <!--parameterType传递pojo类型参数,根据名字查询-->
    <select id="find2" resultType="com.atguigu.domain.User"
            parameterType="com.atguigu.domain.MyQuery">
        select *
        from user
        where username like #{user.username};
    </select>
</mapper>

测试类

public class Test1 {

    private InputStream is;

    private UserDao userDao;
    @Before
    public void init()throws Exception {
        is = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
        userDao=new UserDaoImpl(factory);
    }
    @After
    public void destory()throws Exception{
        is.close();
    }
    /**
     * 测试查询所有的方法
     */
    @Test
    public void test1(){
        List<User> users = userDao.findAll();
        for (User user:users){
            System.out.println(user);
        }
    }
    /**
     * 添加方法
     */
    @Test
    public void test2(){
        User user = new User();
        user.setUsername("张三");
        user.setSex("男");
        user.setAddress("黑龙江");
        user.setBirthday(new Date());
        userDao.saveUser(user);
        //保存后返回用户id
        System.out.println(user.getId());
    }
    /**
     * 删除
     */
    @Test
    public void test3(){
        userDao.deleteUser(41);
    }
    /**
     * 更新
     */
    @Test
    public void test4(){
        User user = new User();
        user.setUsername("李四");
        user.setSex("男");
        user.setAddress("黑龙江");
        user.setBirthday(new Date());
        user.setId(49);
        userDao.updateUser(user);
    }
    /**
     * 根据id查询
     */
    @Test
    public void test5(){
        User user = userDao.findById(49);
        System.out.println(user);

    }
    /**
     * 模糊查询1
     * 第一种配置方式#{username}
     */
    @Test
    public void test6(){
        List<User> users = userDao.findLike("%王%");
        for (User user :users){
            System.out.println(user);
        }
    }
    /**
     * 模糊查询2
     * 第二种配置方式:${value}固定写法
     */
    @Test
    public void test7(){
        List<User> users = userDao.findLike("王");
        for (User user :users){
            System.out.println(user);
        }
    }
    /**
     * 使用聚合函数查询
     * 查询表中有多少条记录数
     */
    @Test
    public void test8(){
        Integer count = userDao.count();
        System.out.println(count);
    }
}

Mybatis后续的学习:
mybatis 事务 | 动态SQL | 多表查询:https://blog.csdn.net/weixin_45606067/article/details/107368642
mybatis延迟加载 | 缓存机制详解:https://blog.csdn.net/weixin_45606067/article/details/107368706
mybatis 注解开发版:https://blog.csdn.net/weixin_45606067/article/details/107368743
mybatis 逆向工程的使用:https://blog.csdn.net/weixin_45606067/article/details/107368781
pageHelper分页技术:https://blog.csdn.net/weixin_45606067/article/details/107368847


如果有收获!!! 希望老铁们来个三连,点赞、收藏、转发
创作不易,别忘点个赞,可以让更多的人看到这篇文章,顺便鼓励我写出更好的博客

猜你喜欢

转载自blog.csdn.net/weixin_45606067/article/details/107368570