Mybatis(黑马)

1.框架概述

课程介绍

三层架构和ssm框架的对应关系

jdbc操作数据库的问题分析

  • jdbc代码回顾
java

public static void main(String[] args) {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
//加载数据库驱动
Class.forName("com.mysql.jdbc.Driver");
//通过驱动管理类获取数据库链接
connection = DriverManager
.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8","ro
ot", "root");
//定义 sql 语句 ?表示占位符
String sql = "select * from user where username = ?";
传智播客——专注于 Java、.Net 和 Php、网页平面设计工程师的培训
北京市昌平区建材城西路金燕龙办公楼一层 电话:400-618-9090
//获取预处理 statement
preparedStatement = connection.prepareStatement(sql);
//设置参数,第一个参数为 sql 语句中参数的序号(从 1 开始),第二个参数为设置的
参数值
preparedStatement.setString(1, "王五");
//向数据库发出 sql 执行查询,查询出结果集
resultSet = preparedStatement.executeQuery();
//遍历查询结果集
while(resultSet.next()){
 System.out.println(resultSet.getString("id")+"
 "+resultSet.getString("username"));
}
} catch (Exception e) {
e.printStackTrace();
}finally{
//释放资源
if(resultSet!=null){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(preparedStatement!=null){
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(connection!=null){
try {
connection.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
上边使用 jdbc 的原始方法(未经封装)实现了查询数据库表记录的操作

在传统的jdbc中,我们为啥实现查询这个功能,需要配置获取连接,获取预处理对象,关闭资源这些操作。但是这些操作是重复的。我们程序员应该将个更多的精力放在解决需求(sql语句(使用sql语句实现需求))上面去,而不是放在这些非业务的操作上去。

mybatis概述

环境搭配--前期准备

  • 数据库和实验用表的准备

CREATE DATABASE mybatis1;


CREATE TABLE `user` (
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `username` VARCHAR(32) NOT NULL COMMENT '用户名称',
  `birthday` DATETIME DEFAULT NULL COMMENT '生日',
  `sex` CHAR(1) DEFAULT NULL COMMENT '性别',
  `address` VARCHAR(256) DEFAULT NULL COMMENT '地址',
  PRIMARY KEY  (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;



INSERT  INTO `user`(`id`,`username`,`birthday`,`sex`,`address`)
 VALUES (41,'老王','2018-02-27 17:47:08','男','北京'),
 (42,'小二王','2018-03-02 15:09:37','女','北京金燕龙'),
 (43,'小二王','2018-03-04 11:34:34','女','北京金燕龙'),
 (45,'传智播客','2018-03-04 12:04:06','男','北京金燕龙'),
 
 
 (46,'老王','2018-03-07 17:37:26','男','北京'),(48,'小马宝莉','2018-03-08 11:44:00','女','北京修正');


SELECT * FROM USER
  • 环境搭建的步骤

  • 可能需要用到的依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>groupId</groupId>
    <artifactId>day1</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging><!--打包方法-->
    <!--依赖-->
    <dependencies>
        <!--mybatis-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.14</version>
        </dependency>

        <!--mysql驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>6.0.6</version>
        </dependency>

        <!--测试-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <!--日志-->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.16</version>
        </dependency>
    </dependencies>


    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

</project>

  • domain
package com.itheima.domain;

import java.io.Serializable;
import java.util.Date;

/*user表对应的实体类*/
public class User implements Serializable {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    public User() {
    }

    public User(Integer id, String username, Date birthday, String sex, String address) {
        this.id = id;
        this.username = username;
        this.birthday = birthday;
        this.sex = sex;
        this.address = address;
    }

    /**
     * 获取
     * @return id
     */
    public Integer getId() {
        return id;
    }

    /**
     * 设置
     * @param id
     */
    public void setId(Integer id) {
        this.id = id;
    }

    /**
     * 获取
     * @return username
     */
    public String getUsername() {
        return username;
    }

    /**
     * 设置
     * @param username
     */
    public void setUsername(String username) {
        this.username = username;
    }

    /**
     * 获取
     * @return birthday
     */
    public Date getBirthday() {
        return birthday;
    }

    /**
     * 设置
     * @param birthday
     */
    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    /**
     * 获取
     * @return sex
     */
    public String getSex() {
        return sex;
    }

    /**
     * 设置
     * @param sex
     */
    public void setSex(String sex) {
        this.sex = sex;
    }

    /**
     * 获取
     * @return address
     */
    public String getAddress() {
        return address;
    }

    /**
     * 设置
     * @param address
     */
    public void setAddress(String address) {
        this.address = address;
    }

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

  • 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">
<!--mybatis主配置文件-->
<configuration>
    <!--配置环境-->
    <environments default="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/mybatis1"/>
                <property name="username" value="root"/>
                <property name="password" value="888888"/>

            </dataSource>
        </environment>
    </environments>
    <!--指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件-->
    <mappers>
        <mapper resource="com/itheima/dao/IUserDao.xml"/>
    </mappers>
</configuration>
  • userDao映射配置文件
<?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.itheima.dao.IUserDao">
   <!--配置查询所有-->
    <select id="findAll">
        select * from user
    </select>
</mapper>

主配置文件和映射配置文件的约束在资料里面有

环境搭配的注意事项

mybatis的入门

package com.itheima.test;





import com.itheima.dao.IUserDao;
import com.itheima.domain.User;
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 javax.annotation.Resource;
import java.io.InputStream;
import java.util.List;

public class MyBatisTest {
    public static void main(String[] args) throws   Exception{
        //1.读取配置文件
        InputStream in =  Resources.getResourceAsStream("sqlMapConfig.xml");
        //2.创建SqlSessionFactory工厂
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(in);
        //3.使用工厂生产sqlSession对象
   SqlSession sqlSession = factory.openSession();
   //4.使用sqlSession创建dao接口的代理对象
   IUserDao userDao = sqlSession.getMapper(IUserDao.class);
   //5.使用代理对象执行方法
        final List<User> users = userDao.findAll();
        for (User user : users) {
            System.out.println(user);
        }
        //6.释放资源
        sqlSession.close();
    }
}

  • 在mybatis3.4.5中需要加上resultType,但是在mybatis 3.5.4就不用加resultType也可以识别出来
  • 将会出现下面的异常

入门案例中的设计模式分析

mybatis注解开发和编写dao实现类的方法

  • 使用注解的方式实现前面的入门案例


    写dao的实现类也可以完成相同的功能,但是让代码过于繁杂,程序员无法将精力集中在业务中

3.自定义mybatis

执行查询的所有分析

  • 前提要求实体类属性和表的列名一致
    关注点1.如何创建代理对象以及使用的设计模式带来的优势2.这个过程中调用的组合关系
    不必关注点:1.jdbc的执行2.xml的解析不需要去深究

    为什么将映射文件中的全类名和SQL语句封装成一个对象而不是参数?
    如果封装成参数,则有多条sql语句的时候不知道谁和谁是对应的(全类名和sql语句是一一对应的)

mybatis自定义编写---根据测试类中缺少的创建类和接口

  • 根据前面的分析进行编写
    首先我们pom.xml中将mybatis删除掉

mybatis自定义编写---解析xml的工具类的介绍

  • 因为xml解析的原理不是很重要,我们使用工具类来完成
    我们直接从资料中导入XMLconfigBuilder用于XML解析



    此时已经基本理解XML对配置文件的解析操作。了解了映射关系
    个人理解:
  • 1.读取主配置文件
    我们将读取主配置文件的信息以流传递给xml解析器:1.先将主配置文件中连接数据库所需要的属性解析出来,并将其的值设置给Configuration配置类中的属性2.根据映射文件的位置找到映射文件(然后开始解析映射文件)3.得到映射文件中所有的mapper标签(一个mapper代表这个dao的一个方法)4.获取映射文件的属性(映射文件的路径)传递给loadMapperConfiguration())函数将映射关系封装成一个MAp集合5 将返回的map对象追加到Configuration配置类的Map集合(专门记录映射关系)中.
  • loadMapperConfiguration(String mapperPath)方法--专门封装映射关系(解析映射文件)
    1.方法中传入了映射文件的路径2.创建map集合 Map<String,Mapper> mappers3. in = Resources.getResourceAsStream(mapperPath)获取文件中的数据到流中4.将流中的数据传入解析器开始解析5.获取namespace(dao全限定列名)作为key的一部分6.获取所有的select标签7.循环遍历select标签,获取id值(方法名)和namespace一起作为key名。得到resultType值和select语句,他们被一起封装成Mapper对象,并将它们作为value值8.将key和value一起封装成mapper对象,并返回

自定义mybatis编码--创建2个默认实现类并分析类之间的操作

实现基于xml查询所有的操作

对mybatis执行过程的概述
本质上只是干了2件事:1.解析XML文件2.创建代理对象
我们将XML文件的信息传入解析器后,解析器将进行解析,得到连接信息(driver,username,password,url)和映射信息(被代理类全类名+方法名和增强的方法)(select语句+返回类型))并将他们封装在Configuration类中

1.根据连接信息将会注册驱动并获取连接对象,并创建预处理对象2.session.getMapper(IUserDao.class);将会创建代理对象3.当调用相应的方法时,将通过映射关系查找出要执行的增强后的方法并执行

对mybatis中代理模式的理解

  • 1.mybatis代理模式来执行sql语句,即在动态生成的代理方法中只是执行SQL,而没有原始方法(没有原始的被代理类)
  • 2.我们的映射关系,可以理解为AOP中的切入点方法和通知方法相互绑定。和代理类中增强部分方法调用原始方法一样,所以我们的映射关系可以抽象成代理类
    (如果我知道动态生成的代理类的结构,也会是这样的结构)
  • 3.既然我们生成的代理类就是一个映射,所以我们也可以说我们的dao是一个映射mapper

Mybatis的CRUD

  • 学习内容

回顾自定义mybatis的流程分析

基于注解的自定义再分析


回顾环境搭配---实现查询所有功能

  • 配置文件的约束都是直接引入的

  • dao
package com.athuima.dao;

import com.athuima.domain.User;

import java.util.List;

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

  • 主配置文件
<?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">
<!--mybatis是主配置文件-->
<configuration>
   <environments default="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/mybatis1?useUnicode=true&amp;characterEncoding=UTF-8"/>
               <property name="username" value="root"/>
               <property name="password" value="888888"/>

           </dataSource>
       </environment>
   </environments>

<!--配置映射文件的位置-->
    <mappers>
        <mapper resource="com/athuima/dao/userDao.xml"></mapper>
    </mappers>


</configuration>
  • 映射配置文件
<?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.athuima.dao.UserDao">
    <select id="findAll" resultType="com.athuima.domain.User">
        select * from user;
    </select>
</mapper>
  • 测试类
 @Test
    public void test() throws Exception{
        //1.读取主配置文件数据
        InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
        //2.创建工厂
        SqlSessionFactory sqlSessionFactory= new SqlSessionFactoryBuilder().build(in);
        //3.利用工厂生成数据库操作对象sqlSession
        final SqlSession sqlSession = sqlSessionFactory.openSession();
        //4.使用sqlSession生成代理对象
        final UserDao userDao = sqlSession.getMapper(UserDao.class);
      //5.使用代理对象执行方法
        final List<User> list = userDao.findAll();
        //6.遍历结果集
        for (User user : list) {
            System.out.println(user);
        }
    }

注意:当我们切换成使用注解时,一定要将原来目录下的映射文件清除或者是换一个位置

保存操作

我们按照xml配置来实现

  • userDao.xml中
<mapper namespace="com.athuima.dao.UserDao">
    <select id="findAll" resultType="com.athuima.domain.User">
        select * from user;
    </select>
    <insert id="save" parameterType="com.athuima.domain.User">/*传入sql语句参数的类型*/
        insert into user (username,sex,birthday,address)values (#{username},#{sex},#{birthday},#{address});
    </insert>
</mapper>
  • 主配置文件
<!--配置映射文件的位置-->
    <mappers>
        <!--使用xml配置-->
        <mapper resource="com/athuima/dao/userDao.xml"></mapper>
        <!--使用注解-->
       <!-- <mapper class="com.athuima.dao.UserDao"></mapper>-->
    </mappers>
  • dao
public interface UserDao {
/*  @Select("select * from user")*/
  List<User>findAll();
  int save(User user);//保存方法
}
  • 测试方法
package com.test;

import com.alibaba.druid.pool.DruidDataSource;
import com.athuima.dao.UserDao;
import com.athuima.domain.User;
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.After;
import org.junit.Before;
import org.junit.Test;



import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.Date;
import java.util.List;

public class MybatisTest {
    private   InputStream in;
    private SqlSession sqlSession;
    private UserDao userDao;
    //抽取关闭资源的方法
    @After//在测试方法之后执行
    public void close() throws Exception {
        //提交事务
        sqlSession.commit();
        sqlSession.close();
        in.close();
    }
    @Before//在测试方法之前执行
    //抽取初始化方法
    public void init() throws Exception{
        //1.读取主配置文件数据
        in = Resources.getResourceAsStream("sqlMapConfig.xml");
        //2.创建工厂
        SqlSessionFactory sqlSessionFactory= new SqlSessionFactoryBuilder().build(in);
        //3.利用工厂生成数据库操作对象sqlSession
        sqlSession = sqlSessionFactory.openSession();
        //4.使用sqlSession生成代理对象
     userDao = sqlSession.getMapper(UserDao.class);
    }
    @Test
    public void testSelect() throws Exception{
        //5.使用代理对象执行方法
        final List<User> list = userDao.findAll();
        //6.遍历结果集
        for (User user : list) {
            System.out.println(user);
        }
    }
    @Test
    public void testSave() throws Exception{
        User user = new User();
        user.setUsername("石文涛");
        user.setSex("男");
        user.setBirthday(new Date());
        user.setAddress("北京");
        //5.使用代理对象执行方法
        final int row = userDao.save(user);
        System.out.println(row);
      
    }
}



当我们执行sql后,没有报错,但是数据的插入没有成功

我们需要在操作的最后进行手动事务提交

修改和删除操作

  • 映射文件
 <!--更新用户-->
    <update id="updateUser" parameterType="com.athuima.domain.User">
        update user set username=#{username},address=#{address},sex=#{sex} ,birthday=#{birthday} where id=#{id};
    </update>
    <!--删除用户-->
    <delete id="deleteUser" parameterType="java.lang.Integer">
        delete from user where id = #{dswwwxsssss}
    </delete>
  • dao
 int updateUser(User user);
  int deleteUser(Integer id);
  • 测试
 @Test
    public void testUpdateUser() throws Exception{
        User user = new User();
        user.setId(53);
        user.setSex("女");
        user.setUsername("火云邪神");
        user.setAddress("汤成一片");
        userDao.updateUser(user);

    }
    @Test
    public void testDeleteUser() throws Exception{
      userDao.deleteUser(51);

    }


其他的操作和前面的一样

查询一个和模糊查询

  • 映射文件
  • dao
 //查询一个用户
  User findById(Integer id);
  /*模糊查询*/
  List<User> findByUsername(String username);
  • 测试
 @Test
    public void testSelectOne() throws Exception{
        final User user = userDao.findById(51);
        System.out.println(user);

    }
    @Test
    public void testFindByUsername() throws Exception{
        final List<User> users = userDao.findByUsername("%王%");
        for (User user : users) {
            System.out.println(user);
        }

    }

查询返回单值操作

  • 映射文件
 <!--查询总数据的条数-->
    <select id="findTotalCount" parameterType="int">
            select count(*) from user;
    </select>
  • dao
    int findTotalCount();

  • 测试

  @Test
    public void testFindTotalCount() throws Exception{
        final int count = userDao.findTotalCount();
        System.out.println(count);
    }

2种模糊查询写法细节分析

保存操作的细节---获取保存数据的id

** SELECT LAST_INSERT_ID();这个sqk语句可以查询出最后保存的id号,需要在你插入操作之后历即调用 **

  • 映射文件
 <!--插入用户-->
    <insert id="save" parameterType="com.athuima.domain.User">/*传入sql语句参数的类型*/
     <!--配置插入操作之后,获取插入数据的Id-->
     <!--order:表明在最后一次插入操作之后执行-->
     <!--keyColumn:对应的列,keyProperty:对应的属性-->
     <selectKey keyColumn="id" keyProperty="id" resultType="int" order="AFTER">
         select last_insert_id();
     </selectKey>
        insert into user (username,sex,birthday,address)values (#{username},#{sex},#{birthday},#{address});
    </insert>
  • 测试
  @Test
    public void testSave() throws Exception{
        User user = new User();
        user.setUsername("张三");
        user.setSex("男");
        user.setBirthday(new Date());
        user.setAddress("日本");
        System.out.println("插入之前:"+user);//打印出保存之前的user
        //5.使用代理对象执行方法
        final int row = userDao.save(user);
        System.out.println("插入之后:"+user);//打印出保存之后的user

    }

mybatis中的参数深入---使用实体类的包装对象作为查询条件

  • 前面查询条件的回顾


    当我的查询条件有多个对象时,我们可以将多个对象包装成一个domain对象作为查询条件

  • 映射文件
<!--使用实体类的包装类作为查询条件-->
    <select id="findByQueryVo" parameterType="com.athuima.domain.QueryVo">
         select * from user where username like #{user.username}

    </select>
  • 查询条件包装类--domain
package com.athuima.domain;
//查询条件的包装类
public class QueryVo {
    private User user;

    public User getUser() {
        return user;
    }

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

  • dao接口
/根据 QueryVo 中的条件模糊查询用户
  List<User> findByQueryVo(QueryVo vo);
  • 测试
@Test
    public void testFindByQueryVo() throws Exception{
        QueryVo vo= new QueryVo();
        User user = new User();
        user.setUsername("王");
        vo.setUser(user);
        final List<User> users = userDao.findByUsername("王");
        for (User u : users) {
            System.out.println(u);
        }

    }

mybatis中返回值的深入----调整实体类的属性解决增和改方法的报错

我们前面在查询的时候要求我们的实体类和数据库的列名一致,但是如果我们不一致会怎样呢

  • 对于非查询操作来说

    此时对于非查询的报错是可预料到的,我们将映射文件中的属性值改成新的属性值,就可以运行了
  • 但是在查询中呢
  <!--查询所有-->
    <select id="findAll" resultType="com.athuima.domain.User">
        select * from user;
    </select>
 @Test
    public void testSelect() throws Exception{
        //5.使用代理对象执行方法
        final List<User> list = userDao.findAll();
        //6.遍历结果集
        for (User user : list) {
            System.out.println(user);
        }
    }

  • 原因分析

我们的属性用户名只是改变了大小写其他的并没有改变,而列名在window系统下不区分大小写所有username可以封装成功

mybatis中返回值深入----解决实体类属性和数据库列名不对应的2种方式

我们封装不成功的原因就是属性名和列名匹配不上,我们需要解决的就是让他们匹配上就可以了

  • 1.第一种方法---起别名
    我可以可以在执行sql语句的时候起别名,让查询出来的列名和属性名一致
  • 查询所有的映射文件
 <!--查询所有-->
    <select id="findAll" resultType="com.athuima.domain.User">
        select id as userId,username as userName,birthday as userBirthday,sex as userSex,address as useraddRess from user;
    </select>
  • 查询结果

发现此时可以封装成功了。这是从sql语句的层面上解决问题,执行的效率是最高的

  • 2.方式2:在映射文件中配置实体类属性和数据库列名的对应关系

  • 映射文件中的配置

   <!--配置查询结果的列名和实体类中的属性名的映射关系-->
    <resultMap id="usermap" type="com.athuima.domain.User">
<!--主键字段的映射-->
        <id property="userId" column="id"></id>
        <!--非主键字段的对应-->
        <result property="userName" column="username"></result>
        <result property="userBirthDay" column="birthday"></result>
        <result property="userSex" column="sex"></result>
        <result property="useraddRess" column="address"></result>
    </resultMap>




    <!--查询所有-->
    <select id="findAll" resultMap="usermap">
      <!--  select id as userId,username as userName,birthday as userBirthday,sex as userSex,address as useraddRess from user;-->
    select * from user;

    </select>

注意此时我们需要将返回的结果resultType改成resultMap表示使用结果映射

  • 这2种方式的说明
    直接在sql语句中修改添加别名,这种效率是最高的,但是这种方法的开发效率比较低,每条查询语句的都需要添加别名。方法2,我们添加结果映射,这将带来多解析一段xml文件,执行效率比较低,但是开发效率比较高

Mybatis 实现 DAO 的传统开发方式(手动写dao的实现类)

mybatis编写dao的实现类---查询列表

  • dao接口
package com.atheima.dao;

import com.atheima.domain.User;

import java.util.List;

public interface UserDao {
    /*查询所有*/
  List<User> findAll();
}

  • dao实现类
package com.atheima.dao.impl;

import com.atheima.dao.UserDao;
import com.atheima.domain.User;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;

import java.util.List;
//UserDao的实现类
public class UserDaoImpl implements UserDao {
    //定义工厂用于创建sqlSession对象
    private SqlSessionFactory sqlSessionFactory;
    public UserDaoImpl(SqlSessionFactory sqlSessionFactory) {
        this.sqlSessionFactory = sqlSessionFactory;
    }
    @Override
    public List<User> findAll() {
        //工厂创建sqlSession对象
        final SqlSession sqlSession = sqlSessionFactory.openSession();
        final List<User> users = sqlSession.selectList("com.atheima.dao.UserDao.findAll");//参数是映射的key
        sqlSession.close();
        return users;
    }
}

  • 映射文件
<mapper namespace="com.atheima.dao.UserDao">
    <select id="findAll" resultType="com.atheima.domain.User">
        select * from user;
    </select>

著配置文件是一样的

  • 测试
public class MyBatisTest {
    private InputStream in;
    private SqlSessionFactoryBuilder builder;
    @After
    public void  close() throws Exception{
        in.close();
    }
    @Before
    public void init() throws Exception{
        //加载配置文件
        in = Resources.getResourceAsStream("sqlMapConfig.xml");
        //2.获取SqlSessionFactory
       builder = new SqlSessionFactoryBuilder();
    }
    @Test
    public void testFindAll() throws Exception{
        //3.创建dao
        UserDao userDao = new UserDaoImpl(builder.build(in));
        final List<User> users = userDao.findAll();
        for (User user : users) {
            System.out.println(user);
        }
    }
}

从这个例子我们可以看出,如果我们使用代理模式动态创建出代理(dao的实现类),这样可以屏蔽很多细节。比如说完全屏蔽了使用sqlSession对象调用selectList方法,这些都是在动态代理中自动完成的。我们从实现类的结构可以看出我们的一个映射(mapper)其实就是一个实现类,所有说我们的动态代理完全可以依据映射关系被动态的创建出来

mybatis编写dao的实现类---保存操作

  • 映射文件
<insert id="saveUser" parameterType="com.atheima.domain.User" >
        insert into user (id,username,birthday,sex,address)values (#{id},#{username},#{birthday},
                                                         #{sex},#{address});

    </insert>
  • dao实现类
  @Override
    public int saveUser(User user) {
        //1.创建session对象
        final SqlSession sqlSession = sqlSessionFactory.openSession();
        //2.执行sql
        final int row = sqlSession.insert("com.atheima.dao.UserDao.saveUser", user);
        //3.提交事务
        sqlSession.commit();
        //4.释放资源
        sqlSession.close();
        return row;
    }
  • dao接口
    int saveUser(User user);

  • 测试类

 @Test
    public void testSaveUser() throws Exception{
        //3.创建dao
        UserDao userDao = new UserDaoImpl(builder.build(in));
        User user = new User();
       user.setUsername("啥问题");
       user.setAddress("曹县");
       user.setBirthday(new Date());
       user.setSex("男");
       userDao.saveUser(user);

    }

mybatis编写dao的实现类---修改删除等其他操作

这些操作和前面的都差不了多少,可以查看IDEA中的代码

mybatis编写dao的实现类的执行过程分析---查询方法

  • 全套代码分析流程看--截图

我们启动debug模式对finfAll()方法的代码去向溯源,我们可以发现mybatis只是对jdbc进行了一层一层封装,sql语句最终的执行还是由jdbc的预处理对象preperedStatement.executer()方法完成的

PreparedStatementHandler

从执行流程可以看出来,不管是增删改操作最后都汇聚到执行PreparedStatementHandler类中的update方法,并且由update方法中的preparedStatement中的execute方法具体执行

mybatis中使用代理dao执行流程的分析


  • 使用动态代理dao执行流程的全部分析

properties标签的使用和细节(将连接信息放在外部属性文件中,然后引入)

  • 外部配置文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis1?useUnicode=true&characterEncoding=UTF-8
jdbc.username=root
jdbc.password=888888
  • 著配置文件
<?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">
<!--mybatis著配置文件-->
<configuration>
    <!--
    配置properties标签
    可以在标签内配置连接数据库的信息。也可以通过属性引用外部配置的属性文件
    resource属性:
    常用于指定配置文件的位置,按照类路径的写法来写,且必须存在于类路径下
    -->
    <!--r-->
    <properties  url="file:D:\ideaprojects\trationmybattis\src\main\resources/jdbcConfig.properties" >
                <!--也可以使用使用类路径  resource=jdbc.properties-->
        <!--配置连接的信息-->
        <!--我们可以在这里写,然后再下面进行引用,但是写在配置文件中然后引用更常见-->
        <!--<property name="driver" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis1?useUnicode=true&amp;characterEncoding=UTF-8"/>
        <property name="username" value="root"/>
        <property name="password" value="888888"/>-->
    </properties>
    <!--mysql环境的配置-->
    <environments default="mysql">
        <environment id="mysql">
            <transactionManager type="JDBC"></transactionManager>
            <!--连接池的配置-->
            <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>

<!--配置映射文件的位置-->
    <mappers>
        <!--使用xml配置文件-->
        <!--我们的mapper也可以使用url属性定位映射文件的位置,前面的道理相同-->
       <mapper  resource="com/itheima/dao/userDao.xml"></mapper>

    </mappers>

</configuration>

typeAliases标签和package标签

  • 所以我们其实也可以给实体类起别名
  • 注意:是在主配置文件中指定的

mybatis第三天

第1章 Mybatis 连接池与事务深入

内容介绍

连接池介绍

连接池就像一个容器,可以将我们的连接初始化放在一个容器里面,我们要用的时候只需要到容器中取就可以了


当取出一个后剩下的将按照队列一样位置全部往前缩进一位

mybatis连接池的介绍

在传统的JDBC中中我们获取连接由DeiverManager接口负责,但是使用DeiverManager来获取连接效率太低了。现在一般使用连接池来管理连接。java提供了DataSourse接口来管理连接池的连接

mybatis中采用unpooled配置连接池的原理分析

通过查看UNPOOLED模型的实现类UnPooledDataSource中关于获取连接的方法,我们可以知道,每次获取连接都需要注册驱动,获取连接,最后是否连接。没有使用到连接池

mybatis中采用pooled配置连接池的原理分析


总体思路就是:见上图

mybatis中的事务原理和自动提交设置




所以其实我们是可以自己手动设置是否自动提交的,如果不设置则默认是关闭自动提交

  • 在创建sqlSession对象的时候可以设置时候自动提交

第二章mybatis映射文件的SQL深入

这个一般是针对查询语句的,当我们的查询条件不同,将不同的查询结果

mybatis中的动态sql语句--if标签

只能使用 and不能使用&&

  • 映射文件
  • 测试
//4.根据传入的条件动态查询
    @Test
    public void testFindByCondition() throws Exception{
        User user = new User();
        user.setUserName("老王");
//        user.setUseraddRess("北京");//也可用于地址
        final List<User> users = userDao.findByCondition(user);
        for (User u : users) {
            System.out.println(u);
        }
    }

mybatis中的动态sql语句--where标签的使用

我们可以使用where标签来替代where1=1这个条件

mybatis中的动态sql语句--foreach和sql标签


我们需要解决的是如何将传入的参数集合赋值给sql语句中in()这个集合中

  • 映射文件
<select id="findUserInIds" parameterType="com.athuima.domain.QueryVo" resultType="com.athuima.domain.User">
select  * from user
        <where>
    <if test="ids!=null and ids.size()>0">
           <foreach collection="ids" open=" and id in(" close=")" item="uid" separator=",">
                #{uid}
           </foreach>
    </if>
        </where>

    </select>
  • 测试
 //根据queryVo中的id集合实现查询用户列表
    @Test
    public void findUserInIds() throws Exception{
        QueryVo vo= new QueryVo();
        List<Integer> ids = new ArrayList<>();
        ids.add(41);
        ids.add(42);
        ids.add(43);
        vo.setIds(ids);
        final List<User> users = userDao.findUserInIds(vo);
        for (User u : users) {
            System.out.println(u);
        }
    }
  • sql标签--抽取重复的ql语句

第三章mysql多表关联查询

mybatis的多表关联查询

完成account表的建立及实现单表查询


建立用户表和账户表


DROP TABLE IF EXISTS `user`;

CREATE TABLE `user` (
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `username` VARCHAR(32) NOT NULL COMMENT '用户名称',
  `birthday` DATETIME DEFAULT NULL COMMENT '生日',
  `sex` CHAR(1) DEFAULT NULL COMMENT '性别',
  `address` VARCHAR(256) DEFAULT NULL COMMENT '地址',
  PRIMARY KEY  (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;



INSERT  INTO `user`(`id`,`username`,`birthday`,`sex`,`address`) VALUES (41,'老王','2018-02-27 17:47:08','男','北京'),(42,'小二王','2018-03-02 15:09:37','女','北京金燕龙'),(43,'小二王','2018-03-04 11:34:34','女','北京金燕龙'),(45,'传智播客','2018-03-04 12:04:06','男','北京金燕龙'),(46,'老王','2018-03-07 17:37:26','男','北京'),(48,'小马宝莉','2018-03-08 11:44:00','女','北京修正');





DROP TABLE IF EXISTS `account`;

CREATE TABLE `account` (
  `ID` INT(11) NOT NULL COMMENT '编号',
  `UID` INT(11) DEFAULT NULL COMMENT '用户编号',
  `MONEY` DOUBLE DEFAULT NULL COMMENT '金额',
  PRIMARY KEY  (`ID`),
  KEY `FK_Reference_8` (`UID`),
  CONSTRAINT `FK_Reference_8` FOREIGN KEY (`UID`) REFERENCES `user` (`id`)-- 和用户表的用户编号绑定外键
) ENGINE=INNODB DEFAULT CHARSET=utf8;



INSERT  INTO `account`(`ID`,`UID`,`MONEY`) VALUES (1,46,1000),(2,45,1000),(3,46,2000);


完成查询所有账户表

  • 主配置文件中指定映射文件的位置
    <mapper resource="com/athuima/dao/accountDao.xml"></mapper>
  • 映射配置文件
<mapper namespace="com.athuima.dao.AccountDao">

    <!--1.查询所有-->
    <select id="findAll" resultType="com.athuima.domain.Account">
    select * from account;

    </select>



</mapper>
  • dao接口

    /**
     * 查询所有账户
     * @return
     */
    List<Account>findAll();
  • 测试
  @Test
   public void testSelect(){
       final List<Account> accounts = accountDao.findAll();
       for (Account account : accounts) {
           System.out.println(account);
       }
   }

完成account一对一操作--通过写account子类方式查询

但是现在我们的要求就是查询所有账户,同时还要获得当前账户所属用户的信息,其实就是实现多表查询
这个问题本质上是在进行多表查询的时候,怎样对查询结果进行封装的问题

  • 我们现在需要取出账户表的全部信息,已经每个账户对应的用户的姓名和地址信息
    我们通过创建子类的方法开拓展实体类,使得其可以封装多表查询的结果,但是这种方法现在并不多见
  • 实体类(继承了Account类)
package com.athuima.domain;

public class AccountUser extends Account{
    private String username;
    private String address;

    public AccountUser() {
    }

    public AccountUser(String username, String address) {
        this.username = username;
        this.address = address;
    }

    /**
     * 获取
     * @return username
     */
    public String getUsername() {
        return username;
    }

    /**
     * 设置
     * @param username
     */
    public void setUsername(String username) {
        this.username = username;
    }

    /**
     * 获取
     * @return address
     */
    public String getAddress() {
        return address;
    }

    /**
     * 设置
     * @param address
     */
    public void setAddress(String address) {
        this.address = address;
    }

    public String toString() {
        return super.toString()+ "AccountUser{username = " + username + ", address = " + address + "}";
    }
}

  • 映射文件
<!--查询所有账户同时包含用户名和地址信息-->
   <select id="findAllAccountUser" resultType="com.athuima.domain.AccountUser">
       select  a.*,u.username,u.address
           from user u join account a on u.id=a.uid;

   </select>

  • 测试
  @Test
    public void findAllAccountUser(){
        final List<AccountUser> accounts = accountDao.findAllAccountUser();
        for (Account account : accounts) {
            System.out.println(account);
        }
    }

完成account一对一操作--建立实体类关系的方式

我们的每个订单它属于一个用户,在表中我们是根据在订单表中设置外键来表示的。在实体类中我们可以在订单表中放置用户的引用来表示这一点
我们的表和实体类会根据属性和列进行自动封装(我们要指定封装的类型),但是当我们在account中添加user对象,这个对象就不能封装可进行了。所以我们必须要为这个查询专门设计resultMap

  • account类(关联一个user对象,实现一对一关系)
package com.athuima.domain;

import java.io.Serializable;

//账户表对应的实体类
public class Account implements Serializable {
    private Integer id;//账户id
    private Integer uid;//用户id
    private Double money;//账户余额
    private User user;//一个账户属于一个用户

    public Account() {
    }

    public Account(Integer id, Integer uid, Double money, User user) {
        this.id = id;
        this.uid = uid;
        this.money = money;
        this.user = user;
    }

    /**
     * 获取
     * @return id
     */
    public Integer getId() {
        return id;
    }

    /**
     * 设置
     * @param id
     */
    public void setId(Integer id) {
        this.id = id;
    }

    /**
     * 获取
     * @return uid
     */
    public Integer getUid() {
        return uid;
    }

    /**
     * 设置
     * @param uid
     */
    public void setUid(Integer uid) {
        this.uid = uid;
    }

    /**
     * 获取
     * @return money
     */
    public Double getMoney() {
        return money;
    }

    /**
     * 设置
     * @param money
     */
    public void setMoney(Double money) {
        this.money = money;
    }

    /**
     * 获取
     * @return user
     */
    public User getUser() {
        return user;
    }

    /**
     * 设置
     * @param user
     */
    public void setUser(User user) {
        this.user = user;
    }

    public String toString() {
        return "Account{id = " + id + ", uid = " + uid + ", money = " + money + ", user = " + user + "}";
    }
}

  • 映射文件
<!--配置结果映射关系-->
    <resultMap id="accountmap" type="com.athuima.domain.Account">
        <!--主键-->
        <id property="id" column="id"></id>
        <result property="uid" column="uid"></result>
        <result property="money" column="money"></result>
        <!--用于指定表方关联的引用实体属性-->
      <association property="user" javaType="com.athuima.domain.User"><!--javaType用于指定关联属性的类型-->
      <id property="id" column="id"></id>
          <result column="username" property="username"/>
          <result column="sex" property="sex"/>
          <result column="birthday" property="birthday"/>
          <result column="address" property="address"/>

      </association>
    </resultMap>

    <!--查询所有账户同时包含用户名和地址信息-->
    <select id="findAllAccountUser" resultMap="accountmap" >
        select  a.*,u.username,u.address
            from user u join account a on u.id=a.uid;

    </select>

  • 测试
 @Test
    public void findAllAccountUser(){
        final List<AccountUser> accounts = accountDao.findAllAccountUser();
        for (Account account : accounts) {
            System.out.println(account);
        }
    }

一对多查询操作

要求查询出所有用户和他们对应的账户信息。一个用户可能对应一对多的关系,我们需要在实体类中实现这个关系

  • 映射文件
<!--建立user表的结果映射-->
    <resultMap type="com.athuima.domain.User" id="userMap">
        <id column="id" property="id"></id>
        <result column="username" property="username"/>
        <result column="address" property="address"/>
        <result column="sex" property="sex"/>
        <result column="birthday" property="birthday"/>
        <!-- collection 是用于建立一对多中集合属性的对应关系
        ofType 用于指定集合元素的数据类型
        -->
        <collection property="accounts" ofType="com.athuima.domain.Account">
            <id column="aid" property="id"/>
            <result column="uid" property="uid"/>
            <result column="money" property="money"/>

        </collection>
    </resultMap>



    <!--1.查询所有-->
    <select id="findAll" resultMap="userMap" >
        select u.*,a.id as aid ,a.uid,a.money from user u left outer join account
            a on u.id =a.uid

    </select>

说明:resultMap的格式(特别是属性的位置还是按照上面的格式写会比较号好),因为可能会出现无法将多个账户封装到一个集合的情况

  • user(和账户关联一对多的关系)
package com.athuima.domain;

import java.util.Date;
import java.util.List;

/**
 * @author SWT
 * @date 2023/12/09
 */
public class User {
private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;
  //一个用户可能对应多个账户
    private List<Account> accounts;


    public User() {
    }

    public User(Integer id, String username, Date birthday, String sex, String address, List<Account> accounts) {
        this.id = id;
        this.username = username;
        this.birthday = birthday;
        this.sex = sex;
        this.address = address;
        this.accounts = accounts;
    }

    /**
     * 获取
     * @return id
     */
    public Integer getId() {
        return id;
    }

    /**
     * 设置
     * @param id
     */
    public void setId(Integer id) {
        this.id = id;
    }

    /**
     * 获取
     * @return username
     */
    public String getUsername() {
        return username;
    }

    /**
     * 设置
     * @param username
     */
    public void setUsername(String username) {
        this.username = username;
    }

    /**
     * 获取
     * @return birthday
     */
    public Date getBirthday() {
        return birthday;
    }

    /**
     * 设置
     * @param birthday
     */
    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    /**
     * 获取
     * @return sex
     */
    public String getSex() {
        return sex;
    }

    /**
     * 设置
     * @param sex
     */
    public void setSex(String sex) {
        this.sex = sex;
    }

    /**
     * 获取
     * @return address
     */
    public String getAddress() {
        return address;
    }

    /**
     * 设置
     * @param address
     */
    public void setAddress(String address) {
        this.address = address;
    }

    /**
     * 获取
     * @return accounts
     */
    public List<Account> getAccounts() {
        return accounts;
    }

    /**
     * 设置
     * @param accounts
     */
    public void setAccounts(List<Account> accounts) {
        this.accounts = accounts;
    }

    public String toString() {
        return "User{id = " + id + ", username = " + username + ", birthday = " + birthday + ", sex = " + sex + ", address = " + address + ", accounts = " + accounts + "}";
    }
}

  • 测试
  @Test
   public void testSelect(){
       final List<User> users = userDao.findAll();
        for (User user : users) {
            System.out.println(user);
        }

   }
  • 直接执行sql语句的情况

分析mybatis的多对多的步骤并搭建环境

  • 角色即身份的意思

多对多--准备角色表的实体类和映射配置

  • 添加role表并插入数据
DROP TABLE IF EXISTS `role`;

CREATE TABLE `role` (
  `ID` INT(11) NOT NULL COMMENT '编号',
  `ROLE_NAME` VARCHAR(30) DEFAULT NULL COMMENT '角色名称',
  `ROLE_DESC` VARCHAR(60) DEFAULT NULL COMMENT '角色描述',
  PRIMARY KEY  (`ID`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;



INSERT  INTO `role`(`ID`,`ROLE_NAME`,`ROLE_DESC`) VALUES (1,'院长','管理整个学院'),(2,'总裁','管理整个公司'),(3,'校长','管理整个学校');




  • 插入中间表--- user--role
DROP TABLE IF EXISTS `user_role`;

CREATE TABLE `user_role` (
  `UID` INT(11) NOT NULL COMMENT '用户编号',
  `RID` INT(11) NOT NULL COMMENT '角色编号',
  PRIMARY KEY  (`UID`,`RID`),
  KEY `FK_Reference_10` (`RID`),
  CONSTRAINT `FK_Reference_10` FOREIGN KEY (`RID`) REFERENCES `role` (`ID`),
  CONSTRAINT `FK_Reference_9` FOREIGN KEY (`UID`) REFERENCES `user` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;

INSERT  INTO `user_role`(`UID`,`RID`) VALUES (41,1),(45,1),(41,2);

查询角色获取角色下属用户信息

多对多是一种表的关系的体现,多对多可以看成是2个一对多,且中间表的列和其他2个表存在外键约束,

因为要查询出来所有用户所以我们需要左外连

  • 映射文件
<!--定义 role 表的 ResultMap-->
<resultMap id="roleMap" type="com.athuima.domain.Role">
    <id property="roleId" column="id"></id>
    <result property="roleName" column="role_name"></result>
    <result property="roleDesc" column="role_desc"></result>
    <collection property="users" ofType="com.athuima.domain.User">
        <id column="uid" property="id"></id>
        <result column="username" property="username"></result>
        <result column="address" property="address"></result>
        <result column="sex" property="sex"></result>
        <result column="birthday" property="birthday"></result>
    </collection>
</resultMap>
<select id="findAll" resultMap="roleMap">
    SELECT
        r.*,u.id uid,
        u.username username,
        u.birthday birthday,
        u.sex sex,
        u.address address
    FROM
        ROLE r
            INNER JOIN
        USER_ROLE ur
        ON ( r.id = ur.rid)
            INNER JOIN
        USER u
        ON (ur.uid = u.id);
</select>
- role类
```java
package com.athuima.domain;

import java.io.Serializable;
import java.util.List;

//角色表对应的实体类
public class Role implements Serializable {
    private  Integer roleId;//角色id
    private  String roleName;//角色名
    private String roleDesc;//角色描述
    //多对多关系
    private List<User> users;

    public List<User> getUsers() {
        return users;
    }

    public void setUsers(List<User> users) {
        this.users = users;
    }

    public Role() {
    }

    public Role(Integer roleId, String roleName, String roleDesc) {
        this.roleId = roleId;
        this.roleName = roleName;
        this.roleDesc = roleDesc;
    }

    /**
     * 获取
     * @return roleId
     */
    public Integer getRoleId() {
        return roleId;
    }

    /**
     * 设置
     * @param roleId
     */
    public void setRoleId(Integer roleId) {
        this.roleId = roleId;
    }

    /**
     * 获取
     * @return roleName
     */
    public String getRoleName() {
        return roleName;
    }

    /**
     * 设置
     * @param roleName
     */
    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }

    /**
     * 获取
     * @return roleDesc
     */
    public String getRoleDesc() {
        return roleDesc;
    }

    /**
     * 设置
     * @param roleDesc
     */
    public void setRoleDesc(String roleDesc) {
        this.roleDesc = roleDesc;
    }

    @Override
    public String toString() {
        return "Role{" +
                "roleId=" + roleId +
                ", roleName='" + roleName + '\'' +
                ", roleDesc='" + roleDesc + '\'' +
                ", users=" + users +
                '}';
    }
}

  • 测试
  public void testSelect() throws Exception{
        //5.使用代理对象执行方法
        final List<Role> roles = roleDao.findAll();
        //6.遍历结果集
        for (Role role : roles) {
            System.out.println(role);
        }
    }


注意看结果体现了多对多的关系

查询用户--获取用户所包含的角色信息

我们的sql语句如果强调使用左右连接可能需要改,其他情况和前面相比,我们的sql都不用修改。我们只需要在user类中添加多对多关系

  • user类
package com.athuima.domain;

import java.util.Date;
import java.util.List;

/**
 * @author SWT
 * @date 2023/12/09
 */
public class User {
private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;
  //多对多关系 :一个用户有多个角色
private List<Role> roles;

    public List<Role> getRoles() {
        return roles;
    }

    public void setRoles(List<Role> roles) {
        this.roles = roles;
    }

    public User() {
    }

    public User(Integer id, String username, Date birthday, String sex, String address) {
        this.id = id;
        this.username = username;
        this.birthday = birthday;
        this.sex = sex;
        this.address = address;

    }

    /**
     * 获取
     * @return id
     */
    public Integer getId() {
        return id;
    }

    /**
     * 设置
     * @param id
     */
    public void setId(Integer id) {
        this.id = id;
    }

    /**
     * 获取
     * @return username
     */
    public String getUsername() {
        return username;
    }

    /**
     * 设置
     * @param username
     */
    public void setUsername(String username) {
        this.username = username;
    }

    /**
     * 获取
     * @return birthday
     */
    public Date getBirthday() {
        return birthday;
    }

    /**
     * 设置
     * @param birthday
     */
    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    /**
     * 获取
     * @return sex
     */
    public String getSex() {
        return sex;
    }

    /**
     * 设置
     * @param sex
     */
    public void setSex(String sex) {
        this.sex = sex;
    }

    /**
     * 获取
     * @return address
     */
    public String getAddress() {
        return address;
    }

    /**
     * 设置
     * @param address
     */
    public void setAddress(String address) {
        this.address = address;
    }

    /**
     * 获取
     * @return accounts
     */
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", birthday=" + birthday +
                ", sex='" + sex + '\'' +
                ", address='" + address + '\'' +
                ", roles=" + roles +
                '}';
    }
}

  • 映射文件
<!--建立user表的结果映射-->
    <resultMap type="com.athuima.domain.User" id="userMap">
        <id column="uid" property="id"></id>
        <result column="username" property="username"/>
        <result column="address" property="address"/>
        <result column="sex" property="sex"/>
        <result column="birthday" property="birthday"/>
       <collection property="roles" ofType="com.athuima.domain.Role">
           <id property="roleId" column="id"></id>
           <result property="roleName" column="role_name"></result>
           <result property="roleDesc" column="role_desc"></result>
       </collection>
    </resultMap>



    <!--1.查询所有用户,并且查出用户所有的角色信息-->
    <select id="findAll" resultMap="userMap" >
        SELECT
            r.*,u.id uid,
            u.username username,
            u.birthday birthday,
            u.sex sex,
            u.address address
        FROM
            ROLE r
                INNER JOIN
            USER_ROLE ur
            ON ( r.id = ur.rid)
                INNER JOIN
            USER u
            ON (ur.uid = u.id);

    </select>

补充内容--JNDI

补充--JDNI的概述和原理

  • JNDI模仿windows的注册表

补充--JDNI搭建maven的war工程



补充--测试JNDI数据源的使用以及以及使用细节

原理还不是很懂,好像这个用的也不多

第四天

今日课程安排

延迟加载和立即加载的概念


我们已查询用户就会把该用户100个账户都查询出来,这对内存无疑是巨大的开销

应该需要的是当我们不需要使用账户的时候就应该把账户查询出来,但是又带来一个问题,那我们需要用的时候不就使用不了了吗

mybatis一对一实现延迟加载

在实际开发中一对一和多对一应该是使用的延迟加载,这里只是举一个例子

  • 查询账户,实现延迟加载用户

延迟加载根据用到才会去加载,才会查询数据库获取数据,如果没有用到关联的数据,不会去查询数据库

  • 主配置文件中:配置mybatis延迟加载
 <settings>
        <!--开启mybatis延迟加载-->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!--允许触发方法进行立即加载 ,否则按需加载-->
        <setting name="aggressiveLazyLoading" value="false"/>
    </settings>
  • 映射配置文件
<!--配置结果映射关系-->
    <resultMap id="accountmap" type="com.athuima.domain.Account">
        <!--主键-->
        <id property="id" column="id"></id>
        <result property="uid" column="uid"></result>
        <result property="money" column="money"></result>
        <!--用于指定表方关联的引用实体属性-->
        <!--select指定的内容:查询用户的唯一标识-->
        <!--column属性指定的内容:用户根据id查询时,所需要参数的值-->
      <association property="user" column="uid" javaType="com.athuima.domain.User" select="com.athuima.dao.UserDao.findById"><!--javaType用于指定关联属性的类型-->
      <!--按照默认封装-->
      </association>
    </resultMap>




    <!--1.查询所有-->
    <select id="findAll" resultMap="accountmap">
        select * from account;

    </select>
  • 账户类
package com.athuima.domain;

import java.io.Serializable;

//账户表对应的实体类
public class Account implements Serializable {
    private Integer id;//账户id
    private Integer uid;//用户id
    private Double money;//账户余额
    private User user;//一个账户属于一个用户

    public Account() {
    }

    public Account(Integer id, Integer uid, Double money, User user) {
        this.id = id;
        this.uid = uid;
        this.money = money;
        this.user = user;
    }

    /**
     * 获取
     * @return id
     */
    public Integer getId() {
        return id;
    }

    /**
     * 设置
     * @param id
     */
    public void setId(Integer id) {
        this.id = id;
    }

    /**
     * 获取
     * @return uid
     */
    public Integer getUid() {
        return uid;
    }

    /**
     * 设置
     * @param uid
     */
    public void setUid(Integer uid) {
        this.uid = uid;
    }

    /**
     * 获取
     * @return money
     */
    public Double getMoney() {
        return money;
    }

    /**
     * 设置
     * @param money
     */
    public void setMoney(Double money) {
        this.money = money;
    }

    /**
     * 获取
     * @return user
     */
    public User getUser() {
        return user;
    }

    /**
     * 设置
     * @param user
     */
    public void setUser(User user) {
        this.user = user;
    }

    public String toString() {
        return "Account{id = " + id + ", uid = " + uid + ", money = " + money ;
    }
}

  • 测试类1





mybatis一对多实现延迟加载

思想是:在需要使用的时候去调用对方配置文件中的配置来实现查询的功能

  • 配置文件
 <resultMap type="com.athuima.domain.User" id="userMap">
        <id column="id" property="id"></id>
        <result column="username" property="username"/>
        <result column="address" property="address"/>
        <result column="sex" property="sex"/>
        <result column="birthday" property="birthday"/>
        <!-- collection 是用于建立一对多中集合属性的对应关系
        ofType 用于指定集合元素的数据类型
        -->
        <collection property="accounts" column="id" ofType="com.athuima.domain.Account" select="com.athuima.dao.AccountDao.findAccountsByUid">
        </collection>
    </resultMap>



    <!--1.查询所有-->
    <select id="findAll" resultMap="userMap" >
      select * from user;

    </select>

  • 账户dao映射配置文件
<!--1.根据用户id查询账户-->
    <select id="findAccountsByUid" resultType="com.athuima.domain.Account">
        select * from account where uid=#{id}

    </select>

测试部分和前面的一样

缓存的概念

适用于缓存的举例:淘宝中商品的库存数,可能真实的数据库中名没有商品了,但是缓存中显示还有商品,但是造成的后果影响不大

mybatis中的一级缓存


当我们查询的是同样的结果,将直接在sqlSession的缓存中提取,而不会到数据库中进行查询


说明我们第二次查询并没有到数据库中查询,而是到sqlSession的缓存中取出的结果,返回了相同的user对象


触发清空一级缓存的情况

使用缓存,缓存和数据库的数据同步是我们需要关注的一个问题

  • 我们在2个查询相同内容的中间,使用update方法进行修改,第二次查询的内容将会是怎样的呢*



  • 只要是update insert delete操作都更新,不需要和查询的内容相同

mybates的二级缓存

  • 二级缓存原理图



    二级缓存的配置







在第二次查询的时候,会创建一个新的user对象,会将二级缓存中的数据填充到新创建的对象里面去。虽然没有发起查询,但是却重写创建了一个对象。所有2个user地下并不相等

mybatis注解开发

手动搭建环境

mybatis注解开发测试和使用注意事项

  • 注意事项

    但是此时同样会报错。也就是注解和使用xml文件,二者不能同时存在
    结论:只要你使用注解开发,但是在你的配置文件路径下同时包含了映射配置文件,此时不管你用不用这个映射配置文件,他都会报错(mybatis内部设置的)

mybatis注解开发保存和更新功能

  • IUserDao接口
package com.itheima.dao;

import com.itheima.domain.User;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;

import java.util.List;

public interface IUserDao {
    /**
     * 查询所有
     * @return
     */
    @Select("select * from user" )
   List<User>findAll();
    /*保存操作*/
    @Insert("insert into user values(#{id},#{username},#{birthday},#{sex},#{address})")
    void save(User user);
    //更新操作
    @Update("update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id}")
    void update(User user);
}

  • 著配置文件
<?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 resource="jdbcConfig.properties"></properties>
    <!--将该包里面所有的类都注册别名-->
    <typeAliases>
        <package name="com.itheima.domain"/>
    </typeAliases>
    <!--JDBC环境配置-->
    <environments default="mysql">

        <environment id="mysql">
            <!--事务名称-->
            <transactionManager type="JDBC"></transactionManager>

            <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>

 <mappers>
     <!--指定该包下面的所有dao接口的映射所在的位置-->
     <package name="com.itheima.dao"/>
 </mappers>



</configuration>
  • 测试类
package com.test;


import com.itheima.dao.IUserDao;
import com.itheima.domain.User;
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.After;
import org.junit.Before;
import org.junit.Test;

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

/*
 *Account表的测试
 * */
public class AnnotationCRUDTest {
    private   InputStream in;
    private SqlSession sqlSession;
    private IUserDao userDao;
    //抽取关闭资源的方法
    @After//在测试方法之后执行
    public void close() throws Exception {
        //提交事务

        sqlSession.commit();
        sqlSession.close();
        in.close();
    }
    @Before//在测试方法之前执行
    //抽取初始化方法
    public void init() throws Exception{
        //1.读取主配置文件数据
        in = Resources.getResourceAsStream("sqlMapConfig.xml");
        //2.创建工厂
        SqlSessionFactory sqlSessionFactory= new SqlSessionFactoryBuilder().build(in);
        //3.利用工厂生成数据库操作对象sqlSession
        sqlSession = sqlSessionFactory.openSession();
        //4.使用sqlSession生成代理对象
        userDao = sqlSession.getMapper(IUserDao.class);
    }
    @Test
    public void testSelect(){
        final List<User> users = userDao.findAll();
        for (User user : users) {
            System.out.println(user);
        }

    }
@Test
    public void testUpdate(){
        User user = new User();
        user.setId(577);
        user.setAddress("北京");
    user.setBirthday(new Date());
    user.setUsername("张三");
    user.setSex("女");
        userDao.update(user);
}
    @Test
    public void testInsert(){
        User user = new User();
        user.setId(577);
        user.setAddress("湖北省十堰市");
        user.setBirthday(new Date());
        user.setUsername("黑马程序员");
        user.setSex("男");
        userDao.save(user);
    }
}

mybatis注解开发CRUD的其他操作

  • dao
package com.itheima.dao;

import com.itheima.domain.User;
import org.apache.ibatis.annotations.*;

import java.util.List;

public interface IUserDao {
    /**
     * 查询所有
     * @return
     */
    @Select("select * from user" )
   List<User>findAll();
    /*保存操作*/
    @Insert("insert into user values(#{id},#{username},#{birthday},#{sex},#{address})")
    void save(User user);
    //更新操作
    @Update("update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id}")

    void update(User user);
    //删除
    @Delete("delete from user where id=#{id}")
    void deleteUser(Integer id);
    //查询单个对象
    @Select("select * from user where id = #{id}")
    User selectOneUser(Integer id);
    //对用户名进行模糊查询
    @Select("select * from user where username like #{username}")
    List<User>selectByUsername(String username);
    //返回表中记录条数
    @Select("select count(*) from user")
    int findTotal();
}

  • 测试
package com.test;


import com.itheima.dao.IUserDao;
import com.itheima.domain.User;
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.After;
import org.junit.Before;
import org.junit.Test;

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

/*
 *Account表的测试
 * */
public class AnnotationCRUDTest {
    private InputStream in;
    private SqlSession sqlSession;
    private IUserDao userDao;

    //抽取关闭资源的方法
    @After//在测试方法之后执行
    public void close() throws Exception {
        //提交事务

        sqlSession.commit();
        sqlSession.close();
        in.close();
    }

    @Before//在测试方法之前执行
    //抽取初始化方法
    public void init() throws Exception {
        //1.读取主配置文件数据
        in = Resources.getResourceAsStream("sqlMapConfig.xml");
        //2.创建工厂
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
        //3.利用工厂生成数据库操作对象sqlSession
        sqlSession = sqlSessionFactory.openSession();
        //4.使用sqlSession生成代理对象
        userDao = sqlSession.getMapper(IUserDao.class);
    }

    @Test
    public void testSelect() {
        final List<User> users = userDao.findAll();
        for (User user : users) {
            System.out.println(user);
        }

    }

    @Test
    public void testUpdate() {
        User user = new User();
        user.setId(577);
        user.setAddress("北京");
        user.setBirthday(new Date());
        user.setUsername("张三");
        user.setSex("女");
        userDao.update(user);
    }

    @Test
    public void testInsert() {
        User user = new User();
        user.setId(577);
        user.setAddress("湖北省十堰市");
        user.setBirthday(new Date());
        user.setUsername("黑马程序员");
        user.setSex("男");
        userDao.save(user);
    }

    @Test
    public void testDelete() {

        userDao.deleteUser(577);
    }

    @Test
    public void testOneUser() {

        final User user = userDao.selectOneUser(48);
        System.out.println(user);
    }
    @Test
    public void testSelectByUsername() {
        final List<User> users = userDao.selectByUsername("%王%");
        for (User user : users) {
            System.out.println(user);
        }
    }

    @Test
    public void testFindTotal() {
        final int total = userDao.findTotal();
        System.out.println(total);
    }
}

mybatis注解建立实体类属性和数据库表中列的对应关系

  • 配置resultMap结果映射(用于替代xml配置中resultMap标签)
  • user类(属性名称和数据库列名不一致)
package com.itheima.domain;

import java.io.Serializable;
import java.util.Date;

public class User implements Serializable {
    private Integer userId;
    private String userName;
    private Date userBirthday;
    private String userSex;
    private String userAddress;

    public User() {
    }

    public User(Integer userId, String userName, Date userBirthday, String userSex, String userAddress) {
        this.userId = userId;
        this.userName = userName;
        this.userBirthday = userBirthday;
        this.userSex = userSex;
        this.userAddress = userAddress;
    }

    /**
     * 获取
     * @return userId
     */
    public Integer getUserId() {
        return userId;
    }

    /**
     * 设置
     * @param userId
     */
    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    /**
     * 获取
     * @return userName
     */
    public String getUserName() {
        return userName;
    }

    /**
     * 设置
     * @param userName
     */
    public void setUserName(String userName) {
        this.userName = userName;
    }

    /**
     * 获取
     * @return userBirthday
     */
    public Date getUserBirthday() {
        return userBirthday;
    }

    /**
     * 设置
     * @param userBirthday
     */
    public void setUserBirthday(Date userBirthday) {
        this.userBirthday = userBirthday;
    }

    /**
     * 获取
     * @return userSex
     */
    public String getUserSex() {
        return userSex;
    }

    /**
     * 设置
     * @param userSex
     */
    public void setUserSex(String userSex) {
        this.userSex = userSex;
    }

    /**
     * 获取
     * @return userAddress
     */
    public String getUserAddress() {
        return userAddress;
    }

    /**
     * 设置
     * @param userAddress
     */
    public void setUserAddress(String userAddress) {
        this.userAddress = userAddress;
    }

    public String toString() {
        return "User{userId = " + userId + ", userName = " + userName + ", userBirthday = " + userBirthday + ", userSex = " + userSex + ", userAddress = " + userAddress + "}";
    }
}

package com.itheima.dao;

import com.itheima.domain.User;
import org.apache.ibatis.annotations.*;

import java.util.List;

public interface IUserDao {
    /**
     * 查询所有
     * @return
     */
    @Select("select * from user" )
    @Results( id = "usermap",
         value = {@Result(id = true,property = "userId",column = "id"),//id用于标记是否为主键(默认为false)
                 @Result(property = "userName",column = "username"),
                 @Result(property = "userBirthday",column = "birthday"),
                 @Result(property = "userSex",column = "sex"),
                 @Result(property = "userAddress",column = "address")
         }
    )

   List<User>findAll();

    //查询单个对象
    @Select("select * from user where id = #{id}")
    @ResultMap("usermap")//引用前面定义的结果映射
    User selectOneUser(Integer id);
    //对用户名进行模糊查询
    @Select("select * from user where username like #{username}")
    @ResultMap({"usermap","selectOneUser"})//引用前面定义的结果映射
    List<User>selectByUsername(String username);


}

这样我们的结果集封装就没问题了

mybatis注解开发一对一查询配置

  • 在这个里面同时设置是否延迟加载
  • dao
public interface IAccountDao {
    //查询所有账户
    @Select("select * from account")
    @Results(id = "accountmap",value = {
            //账户结果映射
            @Result(id = true,property = "id",column = "id"),
            @Result(property = "uid",column = "uid"),
            @Result(property = "money",column = "money"),
            
          @Result(property ="user",column = "uid",one=@One(select = "com.itheima.dao.IUserDao.findById",fetchType = FetchType.EAGER ))//指定不延迟加载
    }
    )
    List<Account> findAll();
  • 测试
  @Test
    public void testSelect() {
        final List<Account> accounts = accountDao.findAll();
        for (Account account : accounts) {
            System.out.println(account);
            System.out.println(account.getUser());
        }

    }


他们的配置原理和xml文件中完全一致

mybatis注解开发一对多查询配置

  • dao
 @Select("select * from user" )
    @Results( id = "usermap",
         value = {@Result(id = true,property = "userId",column = "id"),//id用于标记是否为主键(默认为false)
                 @Result(property = "userName",column = "username"),
                 @Result(property = "userBirthday",column = "birthday"),
                 @Result(property = "userSex",column = "sex"),
                 @Result(property = "userAddress",column = "address"),
                 //根据用户id查询他的用户
                 @Result(property = "accounts",column = "id",many = @Many(select = "com.itheima.dao.IAccountDao.findByUid",fetchType = FetchType.EAGER ))
         }
    )

   List<User>findAll();

  • 测试
   @Test
    public void testSelect() {
        final List<User> users = userDao.findAll();
        for (User user : users) {
            System.out.println(user);
            System.out.println("该用户对应的账户是----------------------------------------------");
            System.out.println(user.getAccounts());
            System.out.println("-------------------------------------------------------------------------------");
        }

    }

mybatis注解开发使用二级缓存

一级缓存 自动开启的





posted @ 2023-12-17 18:40  一往而深,  阅读(847)  评论(0编辑  收藏  举报