三、MyBatis笔记——四种方式使用

文章目录

一、MyBytis框架介绍

1、简介

  1. 基于sql的对象关系映射框架,简称ORM,持久层框架
  2. 对象关系映射:实体类映射为表,属性映射为表的字段,实体类的对象映射为表的记录;实体类之间的关联关系映射为表的关联关系。我们操作实体类对象,就相当于操作表的记录。
  3. 对象关系映射框架还有:Hibernate、MyBatis/MyBatisPluse、SpringData、Ejb(CMP)

2、使用MyBatis相对于传统JDBC的优点

  1. 传统的JDBC开发硬编码,耦合度高。MyBatis使用简单的 XML 或注解来配置,实现了解耦,方便统一管理sql语句;
  2. 提供对象关系映射功能,自动将sql执行结果映射为Java的类,不用我们自己封装
  3. MyBatis使用数据库连接池,也不用在众多程序手动创建连接
  4. MyBatis底层使用JDBC,与各个数据库兼容性高

3、MyBatis的核心组件

  1. mybatis的配置文件mybatis-config.xml文件
  2. Sql映射文件
  3. SqlSessionFactory:加载mybatis-config核心配置文件,连接数据库
  4. SqlSession:用于解析sql映射文件,查找sql映射文件绑定的接口类,调用请求的方法,执行解析的sql语句并根据映射文件中指定的类型返回给调用者。
  5. 参数类型组件ParamateType(@Parm注解描述参数)
  6. 返回类型组件ResultTyp、ResultMap
在映射文件中用于描述接口中方法的参数类型和返回类
<select  id=”接口的方法名称”  ParamateType=”参数的类型” resultType=”返回值的类型”>
sql语句
</select>

4、多种技术之间的区别

  1. MyBatis相比于原始JDBC操作:省去了创建连接对象、sql执行对象、结果集对象、封装结果集为实体类的操作。也通过配置实现了解耦,方便统一管理sql语句
  2. Spring整合MyBatis相比于原始MyBatis:实现了Spring控制反转的思想,将SqlSessionFactory对象通过容器注入程序
  3. SpringBoot整合的MyBstis相比于Spring整合的:省去了编写配置文件或者配置类的步骤,约定优于配置,大部分都采用默认的

二、原始MyBatis开发

1、导入依赖

导入MyBatis和MySQL驱动

<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <version>8.4.0</version>
</dependency>
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.11</version>
</dependency>

2、MyBatis的配置文件内配数据源与sql映射文件

在MyBatis的官网就有模板,<mapper/>标签内写sql映射文件的路径

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
            <!-- 配置数据库的连接信息 -->
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/dataBaseName"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
    <!-- 加载sql的映射文件 -->
        <mapper resource="UserMapper.xml"/>
    </mappers>
</configuration>

3、编写DAO类

public interface StudentDao {
    public int save(Student st);
    public int update(Student st);
    public int delById(Integer id);
    public Student findById(Integer id);
    public int getSumRow();
    public List<Student> findPageAll(@Param("nowPage") Integer nowPage, @Param("pageSize") Integer pageSize);
}

4、编写sql的映射文件

模板也在MyBatis官网

4.1 标签的属性

namespace
  1. mapper标签的属性,绑定dao中的接口,全类名
  2. 做名称空间,名称空间.sql标识区分不同mapper中相同名字的sql语句标识,比如不同的表,但是做查询的sql的id都叫queryById
id
  1. sql语句的标识
  2. 用于绑定接口中的方法(与方法同名)
parameterType

用于指定方法的参数类型

resultType
  1. 查询标签的属性,表示查出来的个体映射为哪个Java类。只有查询的标签有这个属性
  2. 返回值为集合,resultType是集合内的元素类型而不是集合类型。

4.2 sql语句中获取dao方法传递的参数

  1. 参数只有一个,在标签中用parameterType指定;参数大于一个,在接口的参数前面加注解@Param(“参数名”)指定。
  2. 参数是简单数据类型#{参数名};参数是实体对象就是#{对象属性}
int save(Product product);
int delById(Integer pid);
<insert id="save" parameterType="com.jz.entity.Product">
        insert into product(pname,price,num,pdate,cid,photo,tids,note)
        values (#{pname},#{price},#{num},#{pdate},#{cid},#{photo},#{tids},#{note})
</insert>
<delete id="delById" parameterType="java.lang.Integer">
        delete from product where pid=#{pid}
</delete>

4.3 #{}与${}的区别

#{}:进行预编译处理,把参数值设置为 SQL 的参数占位符(例如?),然后在执行SQL语句时用参数值填充占位符。可以避免sql注入。可以处理参数的类型转换。
${}:进行文本替换,将 ${} 中的内容直接替换为参数值或者其他字符串,不会进行预编译,也不会类型转换,适用于需要动态拼接sql的情况(比如分页查询的nowPage和pageSize)。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace属性:绑定接口,作为命名空间 -->
<mapper namespace="com.jz.dao.StudentDao">
    <!--
            id绑定接口中的方法,也是sql的标识;
            parameterType指定方法的参数的类型;
            #{}取出对象的属性值,转为字符串拼接进sql
    -->
    <insert id="save" parameterType="com.jz.entity.Student">
        insert into student(sname,gender,address,birthday,classname,note)
        values(#{sname},#{gender},#{address},#{birthday},#{classname},#{note})
    </insert>
    <update id="update" parameterType="com.jz.entity.Student">
        update student set sname=#{sname},gender=#{gender},
                            address=#{address},
                            birthday=#{birthday},
                            classname=#{classname},
                            note=#{note}
        where stuid=#{stuid}
    </update>
    <delete id="delById" parameterType="java.lang.Integer">
        delete from student where stuid=#{id}
    </delete>
    <select id="findById" parameterType="java.lang.Integer" resultType="com.jz.entity.Student">
        select * from student where stuid=#{id}
    </select>
    <select id="findAll" resultType="com.jz.entity.Student">
        select * from student order by stuid
    </select>
    <select id="findPageAll" resultType="com.jz.entity.Student">
        select * from student order by stuid limit ${(nowPage-1)*pageSize},${pageSize}
    </select>
</mapper>

4.4 使用

public class StudentTest {
    public static void save() throws IOException, ParseException {
        InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        StudentDao mapper = sqlSession.getMapper(StudentDao.class);

        Student student = new Student();
        student.setSname("刘禅");
        student.setGender("男");
        student.setAddress("成都");
        student.setBirthday(new SimpleDateFormat("yyyy-MM-dd").parse("2024-07-26"));
        student.setClassname("java速成班");
        student.setNote("是个乖孩子");

        int result = mapper.save(student);
        if (result>0){
            System.out.println("保存成功");
            sqlSession.commit();//提交事务
        } else {
            System.out.println("保存失败");
            sqlSession.rollback();//回滚事务
        }
        sqlSession.close();
    }

    public static void findAll() throws IOException, ParseException {
        InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        StudentDao mapper = sqlSession.getMapper(StudentDao.class);

        List<Student> all = mapper.findAll();
        all.forEach(student -> {
            System.out.println(student);
        });
        sqlSession.close();
    }
    public static void showPageAll() throws IOException {
        InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        StudentDao mapper = sqlSession.getMapper(StudentDao.class);

        List<Student> all = mapper.findPageAll(2, 5);
        all.forEach(student -> {
            System.out.println(student);
        });
        sqlSession.close();
    }

    public static void main(String[] args) throws IOException, ParseException {
        //save();
        //findAll();
        showPageAll();
    }
}

原始不使用mapper代理,就是直接用sqlSession对象调用insert等方法,通过传递参数命名空间.sql标识执行sql语句
依赖字符串面量

5、Mapper代理开发的好处

  1. 首先它不依赖于字符串字面值,会更安全一点;
  2. 其次,如果你的 IDE 有代码补全功能,那么代码补全可以帮你快速选择到映射好的 SQL 语句。

6、实体类属性与表字段名不一样

比如表的字段是user_id,User实体类属性是uid

6.1 方式一

<resultMap>标签来绑定字段名与属性名的映射,同时<select>标签内要指定resultMap

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.jz.dao.UserDao">
    <select id="findAll" resultMap="findAllResultMap">
        select * from users;
    </select>
<!-- 关联实体类属性与表字段的映射关系 -->
    <resultMap id="findAllResultMap" type="com.jz.entity.Users">
        <result property="uid" column="user_id" />
        <result property="uname" column="user_name" />
        <result property="passwd" column="user_passwd" />
    </resultMap>
</mapper>

6.2 方式二

直接给字段起别名,别名就对应实体类的属性

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.jz.dao.UserDao">
    <select id="findAll" resultType="com.jz.entity.Users">
        select user_id as uid, user_name as uname, user_passwd as passwd from users;
    </select>
</mapper>

7、表的关联映射

将从表的数据映射(封装))到主表的实体类中
之前的是将本表的字段数据自动封装到自己的实体类属性
当表和表之间有主外键关联时,使用resultMap实现关联查询。

7.1 怎么判断主表和从表?

当A表的外键关联B表的主键,那么B表就是主表,A表就是从表

7.2 映射步骤

  1. 为主表的实体类增加一个Set集合属性存储从表数据
    比如Product商品表,有外键cid,关联Catalog种类表的主键cid
    在Catalog的实体类新增一个Set<Product>集合属性存储商品对象
    主表属性
错误做法

Catalog的DAO:public List<Catalog> findAll();
Catalog的sql映射文件

<select id="findAll" resultType="com.entity.Catalog">
        select * from catalog order by cid
</select>

查出来:主表是没有products字段的,不会成功赋值,product属性为null

CatalogDao catalogDao=sqlSession.getMapper(CatalogDao.class);
List<Catalog> catalogList=catalogDao.findAll();
catalogList.forEach(c->{
    System.out.println(c);
});

结果

正确做法:通过resultMap关联对应,将另一个sql的返回结果赋值给这个对象
7.1.2 使用resultMap标签,实现一对多关联映射

延时加载:只有当你用的时候(findAll或findById的时候查从表的属性)才会给他赋值,不用的时候就是null。节省系统资源

<?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.dao.CatalogDao">
    <resultMap id="catalogResultMap" type="com.entity.Catalog">    
    <!--
         collection:一对多的关联映射标签
         property:主表实体类中存储子表的集合属性名
         fetchType="":eager表示加载Catalog类的时候立即加载子表的实体类(默认),lazy延时加载
         column="cid":表示关联子表的字段
         ofType="com.entity.Product":关联的子表实体类
         select="findProductsByCid":查询子表数据的查询方法,将该查询结果的返回值赋值给products属性
    -->
        <collection property="products" fetchType="eager"
                    column="cid"
                    ofType="com.entity.Product"
                    select="findProductsByCid"
        />
    </resultMap>
    <select id="findById" resultMap="catalogResultMap" parameterType="java.lang.Integer">
        select * from catalog where cid=#{cid}
    </select>
    <select id="findAll" resultMap="catalogResultMap">
        select * from catalog order by cid
    </select>
    <select id="findProductsByCid" parameterType="java.lang.Integer" resultType="com.entity.Product">
            select * from product where cid=#{cid}
    </select>
</mapper>

结果

7.2 子表的关联查询

7.2.1 子表的实体类中定义主表的实体类对象作为属性

因为具体的物品对应的主表的记录只有一个,所以不用集合。就比如一部手机的内存大小是固定的,一个物品的种类肯定也是固定的。所以用类对象接受而不是集合。

7.2.2 使用resultMap标签,实现多对一关联映射
<?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.dao.ProductDao">
    <resultMap id="productResultMap" type="com.entity.Product">
        <!-- association标签:多对一的关联映射
             property="catalog":从表中对应的主表的对象属性
             column="cid":外键关联字段
             select="findCataLog":根据哪个方法获取主表对象
        -->
        <association property="catalog"
                     javaType="com.entity.Catalog"
                     column="cid"
                     select="findCataLog"/>
    </resultMap>
    <!-- 方法一:通过resultMap标签 -->
    <select id="findAll" resultMap="productResultMap">
        select * from product where 1=1 order by pid
    </select>
    <!-- 方法二:直接写多表联查的sql,只能用于子表联查 -->
    <!--<select id="findAll" resultType="com.entity.Product">
        select p.*,c.cname from product p,catalog c where p.cid=c.cid
    </select>-->

    <select id="findCataLog" parameterType="java.lang.Integer" resultType="com.entity.Catalog">
        select * from catalog where cid=#{cid}
    </select>
</mapper>

结果

方法2需要在从表实体类中写上需要查的属性,如例中的cname

8、动态sql

  1. choose-when:满足条件的when都执行
    多条件的模糊查询
<!-- select * from employee where 1=1 and ename like '张%' -->
<select id="queryChoose" resultType="com.company.bean.Employee">
	select * from employee where 1=1
	<choose>
		<when test="eName != null">
			and ename like '${eName}%'
		</when>
		<when test="eAddr != null">
			and eAddr like '${eAddr}%'
		</when>
		<otherwise>
			order by ename;
		</otherwise>
	</choose>
</select>
  1. if:
    实现多条件更新
<!-- update employee SET eName = ?, eAddr = ? where eid = ?  -->
<update id="update">
	update employee
	<set>
		<if test="eName!=null">eName = #{eName},</if>
		<if test="ePass!=null">ePass = #{ePass},</if>
		<if test="eAddr!=null">eAddr = #{eAddr}</if>
	</set>
	where eid = #{eid}
</update>
  1. foreach循环执行
    实现批量删除操作:集合参数传递
<!-- delete from employee where eid in ( ? , ? )  -->
<delete id="deleteByList">
	delete from employee where eid in 
	<foreach item="eid" collection="list" open="(" separator="," close=")">
		#{eid}
	</foreach>
</delete>
Java实现
List<Integer> list = new ArrayList<>();
list.add(10020);
list.add(10021);
int i = employeeDao.deleteByList(list);

9、MyBatis的cache缓存

9.1 缓存的概念意义

缓存:缓存是计算机中的一块存储查询结果的区域,在第一次查询后将数据放进去,之后的查询直接从这个区域读取,不频繁访问数据库,加快查询效率。
缓存用于需要频繁访问数据库的单表查询

9.2 mybatis开启cache

在mapper标签内的最顶部加入<cache/>标签开启
开启cache

9.3 缓存分类

9.3.1 一级缓存(默认开启)
9.3.1.1 定义

一级缓存也称本地缓存,作用域是SqlSession对象,表示一次数据库会话

9.3.1.2 原理
  1. mybatis会在SqlSession对象中建立一个简单的缓存,这个缓存是Map集合,sql语句是主键,sql查出来的值是map的值
  2. 进行下一次查询的时候,会先判断是不是跟之前的查询完全相同,也就是判断sql语句(主键)是否相同,相同就直接从map集合中拿
9.3.1.3 生命周期
  1. 会话结束时,SqlSession对象都释放掉
  2. SqlSession对象调用clearCache方法清除缓存
  3. SqlSession对象调用close释放掉
  4. 执行增删改操作是会重建SqlSession的,缓存会清空,因为要更新缓存中的数据
9.3.2 二级缓存

MyBatis的二级缓存是application级别的,应用程序级别,需要通过第三方缓存库来实现
第三方缓存库有内存、系统、磁盘缓存库(EHCache用的比较多的)等等

9.3.2.1 使用二级缓存步骤
  1. 在mybatis的配置文件中开启二级缓存
    开启二级缓存
  2. 对应的sql配置文件设置<cache/>
  3. select标签的属性useCaChe设置为true

三、Spring整合MyBatis——纯注解

spring整合就是将数据源放在了容器中注入mybatis,并且注入代替了创建sqlsessionfactory等的对象
springboot整合连注入都省了,只需要在properties指定一下mybatis的配置文件

1、编写jdbc和mybatis的配置类

jdbc配置类前面有,这里只写mybatis配置类

@MapperScan("com.mapper")
public class MyBatisConfig {
    //注入SqlSessionFactoryBean对象
    @Bean
    public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
        SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
        ssfb.setDataSource(dataSource);
        return ssfb;
    }
}

1.1 注入SqlSessionFactoryBean对象

作用是生成SqlSessionFactory对象,SqlSessionFactory可以生成SqlSession对象操作数据库。
还要为其设置数据源

1.2 为接口创建动态代理对象

@MapperScan():给mybatis配置类上加该注解扫描代理接口所在的包,为扫描的包内的接口创建动态代理对象

2、将jdbc和mybatis配置类导入spring配置类

@Configuration
@ComponentScan("com")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class, MyBatisConfig.class})
public class SpringConfig {
}

3、创建mapper接口

mapper代理接口

4、使用

使用代理对象操作数据库


四、Spring整合MyBatis——xml

1、导入spring-mybatis的依赖

2、编写mybatis的配置文件

在mappers标签下,mapper标签的resource属性指定sql映射文件的位置

<?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>
    <mappers>
        <mapper resource="com/jz/dao/ProductDao.xml" />
        <mapper resource="com/jz/dao/TeZhengDao.xml" />
    </mappers>
</configuration>

3、编辑applicationContext文件

注入SqlSessionFactoryBean对象,设置数据源,设置configLocation为mybatis配置文件的位置

<!-- 注入sqlSessionFactoryBean对象 -->
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>

4、注入mapper扫描器扫dao包

注入MapperScannerConfigurer

<!-- mapper扫描器的注入 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.jz.dao"/>
</bean>

5、编写dao和sql映射文件


五、SpringBoot整合MyBatis

0、两个插件

mybatisplus
easycode-mybatis-helper自动生成代码模板

1、创建项目的时候选上mybatis-framework、mysql-driver、spring data jdbc的依赖

2、编写配置文件

idea数据库环境并在properties配置数据源与mybatis的配置,可以指定datasource的实现为druid

spring.application.name=springboot02
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/studb
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource

#mybaits日志配置
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
#映射文件类型
mybatis.mapper-locations=classpath:mapper/*Dao.xml
#指定实体包的别名
mybatis.type-aliases-package=com.jz.entity

3、利用插件自动生成代码模板

生成模板
选择包和模板
选择模板

4、在dao接口上加@Mapper接口被扫描

5、在启动类上加注解@MapperScan扫描dao

6、修改模板,编程即可

  • 24
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值