首页 > 其他分享 >Mybatis(黑马)

Mybatis(黑马)

时间:2023-12-17 18:45:05浏览次数:616  
标签:username return void id user Mybatis public 黑马

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注解开发使用二级缓存

一级缓存 自动开启的





标签:username,return,void,id,user,Mybatis,public,黑马
From: https://www.cnblogs.com/swtaa/p/17871738.html

相关文章

  • Mybatis-Plus技术教程
    Mybatis-Plus课程目标了解Mybatis-Plus整合Mybatis-Plus通用CRUDMybatis-Plus的配置条件构造器Mybatis-Plus的Service封装代码生成器1Mybatis-Plus介绍1.1Mybatis-Plus介绍MyBatis-Plus(简称MP)是一个MyBatis的增强工具,在MyBatis的基础上只做增强......
  • Springboot整合MybatisPlus
    1、引入mybatis-plus坐标<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.2.0</version></dependency>2、配置数据源spring:datasource:use......
  • 第三章:SpringBoot集成jsp、mybatis的逆向工程和集成
    一、springboot继承jsp二、mybatis逆向工程三、集成mybatis四、案例-集成mybatis五、集成mybatis总结......
  • 带你认识MyBatis持久层框架
    MyBatis是一款优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射。MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。MyBatis可以使用简单的XML或注解来配置和映射原生信息,将接口和Java的POJOs(PlainOldJavaObjects,普通的Java对象)映射成数据......
  • Mybatis 完整的数据库访问过程
    Mybatis完整的数据库访问过程【SessionFactory:连接池,SqlSession:连接】在Mybatis中,SqlSession、Configuration对象以及Mapper之间的关系建立了Mybatis的整个数据访问流程。简要概述如下:通过配置文件(mybatis-config.xml)创建Configuration对象通过Configuration对象......
  • JavaWeb - Day08 - MySQL - 多表查询、事务、索引 - Mybatis - 入门
    01.MySQL-多表查询-概述数据准备#建议:创建新的数据库createdatabasedb04;usedb04;--部门表createtabletb_dept(idintunsignedprimarykeyauto_incrementcomment'主键ID',namevarchar(10)notnulluniquecomment'部门名称',......
  • 12月12日记录mybatis plus的初始学习
    今天开始mybatisplus的初始学习,首先是学习mybatisplus的引入方式需要引入下面三段代码<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.3.1</version></dependency>......
  • SpringBoot+MyBatis-Plus没有扫描到Mapper的问题
    一、问题:WARN22052---[      main]ConfigServletWebServerApplicationContext: NoMyBatismapperwasfoundin'[xxx.xxx.xxxx]'package.Pleasecheckyourconfiguration.WARN22052---[      main]ConfigServletWebServerApplicationConte......
  • MyBatis 批量更新的处理
    一般来讲,在使用MyBatis进行数据库的访问时,通常会遇到需要更新数据的相关业务,在某些业务场景下,如果需要进行一批次的数据更新,可能性能不是特别理想。本文将简要介绍几种能够高效地处理批量更新数据的实现方式单语句的批量更新在某些业务场景下,可能更新的到的数据都在同一个表中......
  • MyBatis-Plus雪花算法实现源码解析
    1.雪花算法(SnowflakeAlgorithm)雪花算法(SnowflakeAlgorithm)是一种用于生成唯一标识符(ID)的分布式算法。最初由Twitter公司开发,用于生成其内部分布式系统中的唯一ID。雪花算法的设计目标是在分布式系统中生成全局唯一的ID,同时保证ID的有序性和趋势递增。雪花算法生成的ID是64......