首页 > 其他分享 >章节二、Mybatis

章节二、Mybatis

时间:2024-11-13 11:44:47浏览次数:1  
标签:章节 username 映射 查询 user Mybatis id select

一、MyBatis框架介绍

1、简介

框架:半成品软件

ORM框架(Object Relation Mapping 对象关系映射):代替jdbc,自动进行对象数据库表之间的转换

MyBatis:半自动的ORM框架,底层是jdbc;不需要写jdbc代码,但需要写sql语句

2、MyBatis使用

环境搭建:

创建项目---添加MyBatis依赖---添加mysql依赖---创建实体类(添加构造器和set、get方法)

mybatis配置文件 SqlMapConfig.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
    PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!--配置环境-->
    <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://ip:3306/database_name"/>
                <property name="username" value="user_name"/>
                <property name="password" value="password"/>
            </dataSource>
        </environment>
    </environments>

</configuration>

创建持久层接口---创建映射文件

持久层 UserMapper.java 如下

package com.codejie.mapper;

import com.codejie.pojo.User;
import java.util.List;

public interface UserMapper {
    /**
     * 查询所有用户
     * @return
     */
    List<User> findAll();
}

映射文件 UserMapper.xml 如下

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.codejie.mapper.UserMapper">
    <select id="findAll" resultType="com.codejie.pojo.User">
        select * from user
    </select>
</mapper>

注册映射文件到SqlMapConfig.xml中

<!--注册映射文件-->
<mappers>
    <mapper resource="com/codejie/mapper/UserMapper.xml"></mapper>
</mappers>

二、MyBatis核心对象及工作流程

1、MyBatis核心对象

SqlSessionFactoryBuilder:工厂构建者

SqlSessionFactory:工厂对象

SqlSession:可以直接操作数据库,也可以创建动态代理来操作数据库

Mapper:持久层接口的代理对象,具体实现了持久层接口,用来操作数据库

2、MyBatis工作流程

SqlSessionFactoryBuilder------构造出------SqlSessionFactory (构造者模式)

SqlSessionFactory-------生产出---------SqlSession (工厂模式)

SqlSession---------创建出----------持久层的代理对象 (动态代理模式)

三、MyBatis增删查改

1、查询

有传入值配传入值的类型,有返回结果配置返回结果的类型(List类型配置泛型)
<select id="findById" parameterType="int" resultType="com.codejie.pojo.User"> select * from user where id=#{id} </select>

2、模糊查询

模糊查询第一种:映射文件中正常写,在业务层传入参数时两端加%,即调用findByUsername("%用户名%")
<select id="findByUsername" parameterType="String" resultType="com.codejie.pojo.User">
    select * from user where username like #{username}
</select>
模糊查询第二种:直接在映射文件中使用%,但是要注意此时用 $ 而不是 # ,并且 $ 无法防止sql注入
<select id="findByUsername" parameterType="String" resultType="com.codejie.pojo.User">
    select * from user where username like '%${username}%'
</select>
模糊查询第三种:通过bind标签在外部定义变量,再绑定到sql中去,此种方法可以防止sql注入
<select id="findByUsername" parameterType="String" resultType="com.codejie.pojo.User">
    <bind name="likename" value="'%' + username + '%'"/>
    select * from user where username like #{likename}
</select>

3、增加

<insert id="add" parameterType="com.codejie.pojo.User">
    insert into user(username,sex,address) values(#{username}, #{sex}, #{address})
</insert>

4、修改

<update id="update" parameterType="com.codejie.pojo.User">
    update user set username=#{username},sex=#{sex},address=#{address} where id=#{id}
</update>

5、删除

<delete id="delete" parameterType="int">
    delete from user where id=#{id}
</delete>

6、多参数传参

顺序传参

按传入顺序分配参数,参数名可以用arg0、arg1...和param1、param2...
<select id="findPage" resultType="com.codejie.pojo.User">
    select * from user limit #{arg0},#{arg1}
</select>
<select id="findPage" resultType="com.codejie.pojo.User">
    select * from user limit #{param1},#{param2}
</select>

参数名传参

在定义接口方法的时候,使用@Param注解定义好参数名
List<User> findPage1(@Param("index") int index, @Param("size") int size);
然后在映射文件中就可以直接使用定义好的参数名
<select id="findPage1" resultType="com.codejie.pojo.User">
    select * from user limit #{index},#{size}
</select>

对象传参:将所需要传入的参数封装进对象

Map传参:

List<User> findPage3(Map<String, Object> params);

<select id="findPage3" resultType="com.codejie.pojo.User" parameterType="map">
    select * from user limit #{index},#{size}
</select>

7、聚合查询和主键回填

聚合查询

select count(id) from user

主键回填

<insert id="add2" parameterType="user">
    <!-- keyProperty:主键属性名,keyColumn:主键列名,resultType:主键类型,order:执行时机 -->
    <selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER">
        SELECT LAST_INSERT_ID();
    </selectKey>
    insert into user(username,sex,address) values(#{username}, #{sex}, #{address})
</insert>

这样插入数据之后,对应的数据对象的id值会被回填补全

@Test
public void testAdd2(){
    User user = new User("程序猿", "男", "西安");
    userMapper.add2(user);
    sqlSession.commit();
    System.out.println(user.getId());
}

四、MyBatis配置文件

1、<properties>标签

通过properties标签引入外部的db.properties文件,就可以在连接信息中直接使用properties中定义的键来表示,将数据库配置放到MyBatis核心配置文件之外,MyBatis核心配置文件很少改动

<configuration>
    <properties resource="db.properties"></properties>
    <!--配置环境-->
    <environments default="mysql">
        <environment id="mysql">
            <!--事务类型-->
            <transactionManager type="JDBC"></transactionManager>
            <!--数据源-->
            <dataSource type="POOLED">
                <!--连接信息-->
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
</configuration>

2、<settings>标签:配置缓存、延迟加载等

<plugins>标签:配置插件

<typeAliases>标签:为类配置一个别名:

<typeAliases>
    <!--为单个类配置别名-->
    <typeAlias type="com.codejie.pojo.User" alias="user"></typeAlias>
    <!--为该包下的所有类省略包名-->
    <package name="com.codejie.pojo"/>
</typeAliases>

加上别名配置后,在映射文件中,就可以直接使用类的别名,而不用带上包名

3、<environments>标签

下有<environment>标签,<environment>标签下有两个标签<transactionManager>和<dataSource>

<environments default="mysql">
  <environment id="mysql">
    <!--type的值说明: JDBC:使用JDBC的提交和回滚 MANAGED:不做事务处理-->
    <transactionManager type="JDBC"></transactionManager>
    <!-- 连接池设置 -->
    <dataSource type="POOLED">
    <!-- 数据源设置... -->
        <property name="driver" value="${driver}"/>
        ...
    </dataSource>
  </environment>
</environments>

4、<mapper>标签

用来注册映射文件,注册映射文件有以下四种方式,选择一种即可

<!--注册映射文件-->
<mappers>
    <!--相对路径-->
    <mapper resource="com/codejie/mapper/UserMapper.xml"></mapper>
    <!--绝对路径-->
    <mapper url="file:///F:\ideaWorkSpace\mybatiscase\mybatisDemo1\src\main\resources\com\codejie\mapper\UserMapper.xml"></mapper>
    <!--注册持久层接口-->
    <mapper class="com.codejie.mapper.UserMapper"/>
    <!--包注册,包中的所有映射文件都被注册-->
    <package name="com.codejie.mapper"/>
</mappers>

五、MyBatis映射文件

1、<resultMap>标签

自定义映射关系:将数据库查询的结果集的列名映射到pojo类的字段名

<resultMap id="teacherMapper" type="com.codejie.pojo.Teacher">
    <!--id定义主键列,property为对应属性名,column为数据库列名-->
    <id property="id" column="tid"></id>
    <!--result定义普通列,property为对应属性名,column为数据库列名-->
    <result property="teacherName" column="tname"></result>
</resultMap>
<!--使用时,引用上面定义的映射的id-->
<select id="findAll" resultMap="teacherMapper">
    select * from teacher
</select>

2、<sql>和<include>标签

使用<sql>标签将需要复用的sql语句储存起来,使用<include>标签在其他地方引用这段sql

<sql id="selectAllField">
    select tid as id, tname as teacherName
</sql>
<select id="findAll" resultType="com.codejie.pojo.Teacher">
    <include refid="selectAllField"></include>
    from teacher
</select>

3、特殊字符处理

符号 实体

<:&lt;

>:&gt;

&:&amp;

':&apos;

":&quot;

六、MyBatis动态sql

1、if:如果条件成立,则拼接if中包含的sql语句

<select id="findByCondition" parameterType="user" resultType="user">
    select * from user where 1=1
    <if test="username != null and username.length() != 0">
        <bind name="likeUsername" value="'%' + username + '%'"/>
        and username like #{likeUsername}
    </if>
    <if test="sex != null and sex.length() != 0"> and sex = #{sex}</if>
    <if test="address != null and address.length() != 0">
        <bind name="likeAddress" value="'%' + address + '%'"/>
        and address like #{likeAddress}
    </if>
</select>

2、where&set:更新数据

<update id="updateUser" parameterType="user">
    update user
    <set>
        <if test="username != null and username.length() != 0">username = #{username}</if>
        <if test="sex != null and sex.length() != 0">sex = #{sex}</if>
        <if test="address != null and address.length() != 0">address = #{address}</if>
    </set>
    <where>
        id = #{id}
    </where>
</update>

3、choose、when、otherwise:条件分支

例:如果username<5则模糊查询;如果5<username<10则精确查询;否则返回id=1的用户

<select id="findByUsername2" resultType="user">
    select * from user
    <where>
        <choose>
            <when test="username.length() &lt; 5">
                <bind name="likeUsername" value="'%'+username+'%'"></bind>
                username like #{likeUsername}
            </when>
            <when test="username.length() &lt; 10">
                username = #{username}
            </when>
            <otherwise>
                id = 1
            </otherwise>
        </choose>
    </where>
</select>

4、forEach:循环

遍历list

<foreach>标签有如下属性:

collection:遍历的对象类型
open:开始的sql语句
close:结束的sql语句
separator:遍历每项间的分隔符
item:表示本次遍历获取的元素,遍历List、Set、数组时表示每项元素,遍历map时表示键值对的值。
index:遍历List、数组时表示遍历的索引,遍历map时表示键值对的键。

例:批量删除用户

<delete id="deleteBatch" parameterType="int">
    delete from user
    <where>
        <foreach open="id in(" close=")" separator="," collection="array" item="id">
            #{id}
        </foreach>
    </where>
</delete>

遍历Collection

例:批量新增

<insert id="insertBatch" parameterType="user">
    insert into user(username,sex,address) values
    <foreach collection="list" item="user" separator=",">
        (#{user.username}, #{user.sex}, #{user.address})
    </foreach>
</insert>

遍历Map

例:传入一个map,根据map中的键值查询User

<select id="findUser" parameterType="map" resultType="user">
    select * from user
    <where>
        <foreach collection="querymap" separator="and" index="key" item="value">
            ${key} = #{value}
        </foreach>
    </where>
</select>

注意:如果遍历的是Map,collection的值不能直接写map,需要在定义接口的时候通过@param参数指定

List<User> findUser(@Param("querymap") Map<String, Object> map);

七、MyBatis缓存

1、介绍

MyBatis执行sql后,会将执行结果缓存下来,下次执行相同的sql时,会从缓存中取

如何判断是相同的sql:

查询的sql语句相同;传递的参数值相同;对结果集的要求相同;预编译的模板id相同

经常查询,但不常改变的数据使用缓存

分为一级缓存和二级缓存

2、一级缓存:

  • MyBatis一级缓存也叫本地缓存。SqlSession对象中包含一个Executor对象,Executor对象中包含一个PerpetualCache对象,在该对象存放一级缓存数据。

  • 由于一级缓存是在SqlSession对象中,所以只有使用同一个SqlSession对象操作数据库时才能共享一级缓存

  • MyBatis的一级缓存是默认开启的,不需要任何的配置。

MyBatis清空一级缓存:

- SqlSession 调用 close() 关闭之后

- SqlSession 调用 clearCache() / commit() 之后

- SqlSession 调用增删改方法后,因为数据发生了改变,则一级缓存会改变

3、二级缓存

  • MyBatis二级缓存也叫全局缓存。数据存放在SqlSessionFactory中,只要是同一个工厂对象创建的SqlSession,在进行查询时都能共享数据。一般在项目中只有一个SqlSessionFactory对象,所以二级缓存的数据是全项目共享的。

  • MyBatis一级缓存存放的是对象,二级缓存存放的是对象的数据。所以要求二级缓存存放的POJO必须是可序列化的,也就是要实现Serializable接口。

  • MyBatis二级缓存默认不开启,手动开启后数据先存放在一级缓存中,只有一级缓存数据清空后,数据才会存到二级缓存中。

注意:SqlSession调用clearCache()会直接清空缓存,无法将数据存到二级缓存中。

- 实体类实现Serializable接口

- 在映射文件中添加<cache>标签开启二级缓存,并设定缓存大小

- 该映射文件中所有接口,一级缓存被销毁后都会默认存二级缓存,(调用clearCache()清除缓存的除外)

八、MyBatis关联查询

关联关系:

例如:一个班级有多个学生,每个学生只属于一个班级。

则在建立学生实体类的时候,需要添加一个所属班级的属性,创建学生表的时候,需要添加一个外键关联班级id;

相反,建立班级实体类的时候,需要添加一个泛型为学生类的List属性,创建班级表的时候,添加一个学生id的字段即可

1、一对一关联查询

例:查询所有学生,需要包含学生的班级信息(1个学生对应1个班级)

在映射文件中,需要添加一个映射;查询时,使用此映射

<resultMap id="studentMapper" type="com.codejie.pojo.Student">
    <!--主键-->
    <id column="sid" property="sid"></id>
    <!--普通列-->
    <result property="name" column="name"></result>
    <result property="age" column="age"></result>
    <result property="sex" column="sex"></result>
    <!--关联对象列  property:属性名 classId:关联列名  javaType:对象名-->
    <association property="classes" column="classId" javaType="com.codejie.pojo.Classes">
        <!--关联对象主键列-->
        <id property="cid" column="cid"></id>
        <!--关联对象普通列-->
        <result property="className" column="className"></result>
    </association>
</resultMap>
<select id="findAll" resultMap="studentMapper">
    select * from student left join classes on student.classId = classes.cid;
</select>

2、一对多关联查询

例:查询所有班级,需要包含班级中所有的学生(1个班级中有多个学生)

<resultMap id="classesMapper" type="com.codejie.pojo.Classes">
    <id property="cid" column="cid"></id>
    <result property="className" column="className"></result>
    <!--集合列 property:属性名 column:关联列名 ofType:泛型类型-->
    <collection property="studentList" column="classId" ofType="com.codejie.pojo.Student">
        <id property="sid" column="sid"></id>
        <result property="name" column="name"></result>
        <result property="age" column="age"></result>
        <result property="sex" column="sex"></result>
    </collection>
</resultMap>
<select id="findAll" resultMap="classesMapper">
    select * from classes left join student on classes.cid = student.classId
</select>

3、多对多关联查询

例:查询所有的老师和老师所教的班级(1个老师可以教多个班级,1个班级有多个老师任课)

多对多本质就是,两边都是1对多

<resultMap id="teacherMapper" type="com.codejie.pojo.Teacher">
    <id property="tid" column="tid"></id>
    <result property="tname" column="tname"></result>
    <!--集合列 property:属性名 column:关联列名 ofType:泛型类型-->
    <collection property="classesList" column="tid" ofType="com.codejie.pojo.Classes">
        <id property="cid" column="cid"></id>
        <result property="className" column="className"></result>
    </collection>
</resultMap>
<select id="findAll" resultMap="teacherMapper">
    select *
    from teacher
    left join classes_teacher on teacher.tid = classes_teacher.tid
    left join classes on classes.cid = classes_teacher.cid
</select>

例2:查询所有班级信息,包含学生和老师信息

<resultMap id="classesMapper" type="com.codejie.pojo.Classes">
    <id property="cid" column="cid"></id>
    <result property="className" column="className"></result>
    <!--集合列 property:属性名 column:关联列名 ofType:泛型类型-->
    <collection property="studentList" column="classId" ofType="com.codejie.pojo.Student">
        <id property="sid" column="sid"></id>
        <result property="name" column="name"></result>
        <result property="age" column="age"></result>
        <result property="sex" column="sex"></result>
    </collection>
    <collection property="teacherList" column="cid" ofType="com.codejie.pojo.Teacher">
        <id property="tid" column="tid"></id>
        <result property="tname" column="tname"></result>
    </collection>
</resultMap>
<select id="findAll" resultMap="classesMapper">
    select *
    from classes
    left join student on classes.cid = student.classId
    left join classes_teacher on classes.cid = classes_teacher.cid
    left join teacher on teacher.tid = classes_teacher.tid
</select>

九、MyBatis分解式查询

1、介绍

将一个关联查询的sql语句,分解成多个单表查询的sql语句

分解查询也叫 N+1 查询

连接查询:

  • 优点:降低查询次数,从而提高查询效率。
  • 缺点:如果查询返回的结果集较多会消耗内存空间。

N+1查询:

  • 优点:结果集分步获取,节省内存空间。
  • 缺点:由于需要执行多次查询,相比连接查询效率低。.

2、一对多的N+1查询

例:查询班级下的学生(1个班级下有多个学生)

- 在学生映射文件中,定义好根据班级id查询学生的方法的映射

<select id="findByClassId" resultType="com.codejie.pojo.Student" parameterType="int">
    select * from student where classId = #{classId}
</select>

- 在班级映射文件中,自定义映射,通过调用根据班级id查询学生的方法来此班级下的学生,传入班级id

<!--自定义映射-->
<resultMap id="myClassesMapper" type="com.codejie.pojo.Classes">
    <id property="cid" column="cid"></id>
    <result property="className" column="className"></result>
    <!-- property:属性名  ofType:泛型  select:从表查询调用的方法  column:调用方法传入的字段-->
    <collection property="studentList"
                ofType="com.codejie.pojo.Student"
                select="com.codejie.mapper.StudentMapper2.findByClassId"
                column="cid">
    </collection>
</resultMap>
<select id="findAll" resultMap="myClassesMapper">
    select * from classes
</select>

3、一对一的N+1查询

例:查询学生和所属班级(1个学生只属于1个班级)

- 同样,在班级映射文件中,定义好根据班级id查询班级信息的接口映射

<select id="findByCid" resultType="com.codejie.pojo.Classes" parameterType="int">
    select * from classes where cid = #{cid}
</select>

- 然后在学生映射文件中,通过调用根据班级id查询班级信息的方法,传入班级id,查询班级信息

<resultMap id="myStudentMapper" type="com.codejie.pojo.Student">
    <id property="sid" column="sid"></id>
    <result property="name" column="name"></result>
    <result property="age" column="age"></result>
    <result property="sex" column="sex"></result>
    <!--映射对象列-->
    <association property="classes" javaType="com.codejie.pojo.Classes"
                 select="com.codejie.mapper.ClassesMapper2.findByCid"
                 column="classId">
    </association>
</resultMap>
<select id="findAll" resultMap="myStudentMapper">
    select * from student
</select>

4、分解式查询的延迟加载

  • 立即加载:在查询主表时就执行所有的Sql语句。
  • 延迟加载:又叫懒加载,首先执行主表的查询语句,使用从表数据时才触发从表的查询语句

开启延迟加载:

设置所有的N+1查询都为延迟加载

<settings>
	<!--为所有N+1查询开启懒加载(一般不用这种方式)-->
	<setting name="lazyLoadingEnabled" value="true"/>
</settings>

设置某个方法为懒加载:

在映射文件的<association>、<collection>中添加fetchType属性,属性值 lazy:懒加载;eager:立即加载

<resultMap id="myClassesMapper" type="com.codejie.pojo.Classes">
	<id property="cid" column="cid"></id>
	<result property="className" column="className"></result>
	<!-- property:属性名  ofType:泛型  select:从表查询调用的方法  column:调用方法传入的字段 fetchType:加载类型-->
	<collection property="studentList"
				ofType="com.codejie.pojo.Student"
				select="com.codejie.mapper.StudentMapper2.findByClassId"
				column="cid" fetchType="lazy">
	</collection>
</resultMap>
<select id="findAll" resultMap="myClassesMapper">
	select * from classes
</select>

在MyBatis配置文件中添加一个配置项 lazyLoadTriggerMethods 可以指定调用懒加载对象的什么方法时触发加载,为空则调用任何方法都不触发加载

<settings>
	<!--指定对象的什么方法触发懒加载-->
	<setting name="lazyLoadTriggerMethods" value=""/>
</settings>

十、MyBatis注解开发

1、环境搭建

- 以前是注册映射,现在不需要映射文件,直接注册接口

- 通过注解定义接口

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

2、增删改查

增加数据 & 主键回填:在定义接口时添加注解 Insert 和 SelectKey ;注解参数与映射文件标签参数一致

@SelectKey(keyColumn = "id",keyProperty = "id",resultType = int.class,before = false,statement = "SELECT LAST_INSERT_ID()")
@Insert("insert into user(username,sex,address) values(#{username},#{sex},#{address})")
void add(User user);

修改数据 & 删除数据

    @Update("update user set username=#{username},sex=#{sex},address=#{address} where id=#{id}")
    void update(User user);

    @Delete("delete from user where id=#{id}")
    void delete(int id);

模糊查询

    @Select("select * from user where username like concat('%',#{username},'%')")
    List<User> findByUsernameLike(String username);

3、动态SQL

注意动态sql还是只能通过拼接的方式拼接到Select注解中,并且一定要使用<script>标签包裹起来

    @Select("<script>" +
            "  select * from user\n" +
            "     <where>\n" +
            "       <if test=\"username != null and username.length() != 0\">\n" +
            "         username like #{username}\n" +
            "       </if>\n" +
            "       <if test=\"sex != null and sex.length() != 0\">\n" +
            "         and sex = #{sex}\n" +
            "       </if>\n" +
            "       <if test=\"address != null and address.length() != 0\">\n" +
            "         and address = #{address}\n" +
            "       </if>\n" +
            "     </where>" +
            "</script>")
    List<User> findByCondition(User user);

一般不建议使用注解编写动态sql,而是使用方法来构建动态sql

创建动态生成sql的类:UserProvider, 编写生成sql的方法

public class UserProvider {
    //根据任意条件查询的sql语句
    public String findByConditionSql(User user){
        StringBuffer sb = new StringBuffer("select * from user where 1=1");
        if (user.getUsername() != null && user.getUsername().length() != 0){
            sb.append(" and username like concat('%',#{username},'%')");
        }
        if (user.getSex() != null && user.getSex().length() != 0){
            sb.append(" and sex=#{sex}");
        }
        if (user.getAddress() != null && user.getAddress().length() != 0){
            sb.append(" and address like concat('%',#{address},'%')");
        }
        return sb.toString();
    }
}

编写接口时,使用@SelectProvider注解

    @SelectProvider(type = UserProvider.class, method = "findByConditionSql")
    List<User> findByCondition(User user);

4、自定义映射

使用@Results注解定义映射,使用@ResultMap注解来使用映射

    @Results(id = "userMapper", value = {
            @Result(id = true, property = "id", column = "id"),
            @Result(property = "username", column = "username"),
            @Result(property = "sex", column = "sex"),
            @Result(property = "address", column = "address"),
    })
    @Select("select * from user")
    List<User> findAll();

    @ResultMap("userMapper")
    @Select("select * from user where id=#{id}")
    User findById(int id);

5、开启二级缓存

在持久层接口上加@CacheNamespace(blocking=true)代表该接口下所有方法都开启二级缓存

// 开启二级缓存
@CacheNamespace(blocking = true)
public interface UserMapper {…}

6、关联查询

在注解式开发中,只支持N+1的关联查询

一对一的关联查询

例如:查询所有学生信息和他所属的班级信息

先在ClassesMapper接口中编写按id查询班级信息的方法

    @Select("select * from classes where cid=#{cid}")
    Classes findByCid(int cid);

然后在StudentMapper接口中编写获取所有学生信息的方法,并定义映射关系

    @Results(id = "studentMapper", value = {
            @Result(id = true, property = "sid", column = "sid"),
            @Result(property = "name", column = "name"),
            @Result(property = "age", column = "age"),
            @Result(property = "sex", column = "sex"),
            /**
             * property:属性名
             * column:调用从表方法时,需要传入的列名
             * one:代表1对1关联
             *  select:调用从表获取数据的方法名
             *  fetchType:加载方式(1对1立即加载,1对多懒加载)
             */
            @Result(property = "classes", column = "classId",
                    one = @One(select = "com.codejie.mapper.ClassesMapper.findByCid",fetchType = FetchType.EAGER))
    })
    @Select("select * from student")
    List<Student> findAll();

一对多关联查询

例如:查询所有班级信息和班级下的学生信息

先在StudentMapper接口中添加按学生id查询学生信息的方法

    //根据班级id查询学生
    @Select("select * from student where classId=#{classId}")
    List<Student> findByClassId(int classId);

然后在ClassesMapper接口中添加查询全部班级的方法,并定义映射关系

    @Results(id = "classMapper", value = {
            @Result(id = true, property = "cid", column = "cid"),
            @Result(property = "className", column = "className"),
            /**
             * property:属性名
             * column:调用从表方法时,需要传入的列名
             * many:代表1对多关联
             *  select:调用从表获取数据的方法名
             *  fetchType:加载方式(1对1立即加载,1对多懒加载)
             */
            @Result(property = "studentList", column = "cid",
            many = @Many(select = "com.codejie.mapper.StudentMapper.findByClassId", fetchType = FetchType.LAZY))
    })
    @Select("select * from classes")
    List<Classes> findAll();

7、注解开发和映射文件开发对比

映射文件:

  • 代码与Sql语句是解耦的,修改时只需修改配置文件,无需修改源码。
  • Sql语句集中,利于快速了解和维护项目。
  • 级联查询支持连接查询和分解查询两种方式,注解开发只支持分解查询。

注解开发:

  • 配置简单,开发效率高。
  • 类型安全,在编译期即可进行校验,不用等到运行时才发现错误。

十一、分页插件PageHelper

1、使用

- 引入依赖:在pom.xml中引入pagehelper

- 配置插件:在MyBatis配置文件中添加插件配置

    <plugins>
        <!-- 配置分页插件 -->
        <plugin interceptor="com.github.pagehelper.PageInterceptor">
            <!-- 设置数据库类型-->
            <property name="helperDialect" value="mysql"/>
        </plugin>
    </plugins>

- 使用

    //测试分页插件
    @Test
    public void testFindPage(){
        //设置分页参数
        PageHelper.startPage(1,3);
        //正常查询
        List<User> all = userMapper.findAll();
        //封装查询结果,生成页面数据
        PageInfo pageInfo = new PageInfo(all);
        //打印
        System.out.println("结果集:"+pageInfo.getList());
        System.out.println("总条数:"+pageInfo.getTotal());
        System.out.println("当前页:"+pageInfo.getPageNum());
        System.out.println("每页条数:"+pageInfo.getPageSize());
    }

十二、MyBatis Generator工具引入

官方提供的工具,根据数据库表结构自动生成POJO累,持久层接口和映射文件

- 插件引入

    <build>
        <plugins>
            <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.3.7</version>
                <!--MBG配置-->
                <configuration>
                    <!--MBG胚子文件位置-->
                    <configurationFile>src/main/resources/generatorConfig.xml</configurationFile>
                    <!--运行时显示详情-->
                    <verbose>true</verbose>
                    <!--允许覆盖文件-->
                    <overwrite>true</overwrite>
                </configuration>
            </plugin>
        </plugins>
    </build>

编写插件配置文件 generatorConfig.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
    <!--配置JDBC的jar包位置-->
    <classPathEntry location="F:\XXXX\repository\mysql\mysql-connector-java\8.0.22\mysql-connector-java-8.0.22.jar"/>

    <context id="default" targetRuntime="MyBatis3">
        <!-- 是否去除自动生成的注释-->
        <commentGenerator>
            <property name="suppressAllComments" value="true"/>
        </commentGenerator>

        <!--数据库连接参数-->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                        connectionURL="jdbc:mysql://HOST:PORT/DBNAME"
                        userId="USERNAME"
                        password="PASSWORD"></jdbcConnection>

        <!-- 类型处理器,在数据库类型和java类型之间的转换控制-->
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false"/>
        </javaTypeResolver>

        <!-- targetProject:POJO类路径  targetProject:生成的POJO类的包-->
        <javaModelGenerator targetProject="src/main/java" targetPackage="com.codejie.pojo">
            <!-- 是否生成子包 -->
            <property name="enableSubPackages" value="false"/>
            <!-- 设置是否在getter方法中,对String类型字段调用trim()方法 -->
            <property name="trimStrings" value="true"/>
        </javaModelGenerator>

        <!-- targetProject:配置文件路径 targetPackage:生成映射文件的位置 -->
        <sqlMapGenerator targetProject="src/main/resources" targetPackage="com.codejie.mapper">
            <!-- 是否生成子包 -->
            <property name="enableSubPackages" value="false"/>
        </sqlMapGenerator>

        <!-- targetPackage:JAVA类路径 targetProject:生成的持久层接口包 -->
        <javaClientGenerator targetProject="src/main/java" targetPackage="com.codejie.mapper" type="XMLMAPPER">
            <!-- 是否生成子包 -->
            <property name="enableSubPackages" value="false"/>
        </javaClientGenerator>

        <!-- 数据库表,表名不要和其他库中的表名一样 -->
        <table tableName="product"></table>
    </context>

</generatorConfiguration>

刷新maven,然后运行插件,插件会自动创建好接口和映射文件

  • Product.java:POJO类

  • ProductMapper.java:持久层接口

  • ProductMapper.xml:映射文件

  • ProductExample.java:查询扩展类,该类可以构造复杂的查询条件。

    • Criterion:代表一个字段。
    • GeneratedCriteria:抽象类,生成查询条件的工具。
    • Criteria:GeneratedCriteria的子类,生成查询条件的工具。

最后记得在MyBatis配置文件中注册生成的映射文件

使用生成的方法时

- 增删查改直接调用对应方法即可

- 主键查询可以调用:selectByPrimaryKey() 传入id即可

- 条件查询(例如查询所有或者根据某些条件查询,调用 selectByExample(),传入对应pojo类的扩展类 xxExample

添加条件通过 createCriteria() 方法创建内部类对象,然后调用各种 and方法来添加条件

    @Test
    public void testFindByNameLike(){
        //使用查询扩展对象,构建查询条件
        ProductExample productExample = new ProductExample();
        //构建查询条件
        productExample.createCriteria().andProductnameLike("%苹果%").andPriceBetween(50.0,100.0);
        List<Product> products = productMapper.selectByExample(productExample);
        products.forEach(System.out::println);
    }

- 复杂查询:and查询可以直接在criteria对象后面.andxxx;但是or查询则需要创建两个criteria对象,然后将第二个criteria对象放到xxxExample.or(criteria2)方法中

    @Test
    public void testFindExample(){
        //使用查询扩展对象,构建查询条件
        ProductExample productExample = new ProductExample();
        //构建查询条件
        ProductExample.Criteria criteria1 = productExample.createCriteria();
        criteria1.andProductnameLike("%苹果");
        ProductExample.Criteria criteria2 = productExample.createCriteria().andPriceBetween(100.0, 200.0);
        productExample.or(criteria2);
        List<Product> products = productMapper.selectByExample(productExample);
        products.forEach(System.out::println);
    }

十三、动态代理

1、介绍

代理模式:代理模式是23种设计模式之一,代理模式的作用是在不修改原对象的基础上增强该对象的方法

代理模式分为静态代理、动态代理。静态代理会生成一个代理类,动态代理不会生成代理类,直接生成代理对象。

2、写法

jdk动态代理和CGLib动态代理

jdk动态代理:针对接口进行代理

例如:定义一个原类型Apple,为苹果手机专卖店,有售卖方法sell和售后方法repair,现在需要用动态代理方式增强这个苹果手机专卖店,例如在售卖手机时打折和提供充电头,售后时安排专属客服

//被代理接口
public interface Apple {
    String sell(double price);//卖产品
    void repair();//维修
}
public class AppleImpl implements Apple{
    public String sell(double price) {
        System.out.println("产品卖了"+price+"元");
        return "iphone13";
    }

    public void repair() {
        System.out.println("苹果售后维修");
    }
}

编写代理方法类

//代理方式类,定义被代理对象的增强方式
//该类实现InvocationHandler接口,重写invoke方法,定义方法的增强方式
public class ShoppingProxy implements InvocationHandler {
    private Apple apple; //被代理对象
    public ShoppingProxy(Apple apple){
        this.apple = apple;
    }

    /**
     * 定义原方法的增强方式
     * @param proxy:被代理对象
     * @param method:被代理对象调用的方法
     * @param args:被代理对象调用方法时传入的参数
     * @return:方法的返回值
     * @throws Throwable
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //使用反射技术
        String name = method.getName();//被代理对象执行的方法名
        if ("sell".equals(name)){
            double price = (Double) args[0]*0.9;//增强参数
            Object result = method.invoke(apple, price);//执行方法
            return result + "和充电头"; //返回值增强
        }else if("repair".equals(name)){
            System.out.println("专属客服为您服务");//增强放发的流程
            return method.invoke(apple,args);
        }else {
            return method.invoke(apple,args);//什么都不增强
        }
    }
}

使用jdk代理增强后的被代理对象

public class TestJDK {
    public static void main(String[] args) {
        //被代理对象
        Apple apple = new AppleImpl();
        //代理方式对象
        ShoppingProxy shoppingProxy = new ShoppingProxy(apple);
        //生成代理对象
        Apple appleJD = (Apple) Proxy.newProxyInstance(
                apple.getClass().getClassLoader(),//获取类加载器
                apple.getClass().getInterfaces(),//被代理接口
                shoppingProxy//代理方式对象
        );
        //京东苹果店售卖
        String sell = appleJD.sell(6000.00);
        System.out.println(sell);
        //京东苹果店售后
        appleJD.repair();
    }
}

CGLib动态代理:针对类进行代理

- 引入依赖

        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.3.0</version>
        </dependency>
    </dependencies>
创建代理类
//被代理类
public class Apple {
    public String sell(double price) {
        System.out.println("产品卖了"+price+"元");
        return "iphone13";
    }

    public void repair() {
        System.out.println("苹果售后维修");
    }
}

创建代理方法类

//代理方式类,实现MethodInterceptor接口,重写intercept方法
public class ShoppingProxy implements MethodInterceptor {
    private Apple apple;

    public ShoppingProxy(Apple apple) {
        this.apple = apple;
    }

    /**
     * 定义原方法的增强方式
     * @param o:被代理对象
     * @param method:被代理对象调用的方法
     * @param objects:被代理对象调用方法时传入的参数
     * @param methodProxy:底层生成的代理类的引用
     * @return:方法的返回值
     * @throws Throwable
     */
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        String name = method.getName();//获取方法名
        if ("sell".equals(name)){
            double price = (Double) objects[0] * 0.8;
            Object result = method.invoke(apple, price);
            return result+"和数据线";
        }else if ("repair".equals(name)){
            System.out.println("专属客服为您服务");
            return method.invoke(apple,objects);
        }else {
            return method.invoke(apple,objects);
        }
    }
}

使用cglib增强后的被代理对象

public class TestCGLib {
    public static void main(String[] args) {
        //被代理对象
        Apple apple = new Apple();
        //代理方式对象
        ShoppingProxy shoppingProxy = new ShoppingProxy(apple);
        //生成代理对象
        Apple appleTB = (Apple) Enhancer.create(Apple.class, shoppingProxy);

        //执行增强后的方法
        String sell = appleTB.sell(6000.0);
        System.out.println(sell);
        appleTB.repair();
    }
}

标签:章节,username,映射,查询,user,Mybatis,id,select
From: https://www.cnblogs.com/jieety/p/18513774

相关文章

  • 章节一、Maven学习
    一、maven介绍1、简介Maven是一个项目管理工具(构建工程、管理jar包、编译代码、完成测试、项目打包)Maven是基于POM(projectobjectmodel项目对象模型)实现的Maven标准化了项目结构Maven提供了一个免费的中央仓库开源jar包Maven是跨平台的2、作用一键构建:编译、测试、打包......
  • MyBatis及相关文件配置
    MyBatis是一款优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射。以下是对MyBatis的详细讲解:一、MyBatis的起源与发展MyBatis最初是Apache的一个开源项目iBATIS,2010年迁移到GoogleCode并改名为MyBatis,2013年11月又迁移到GitHub。MyBatis的最新版本是3.5.13,发布于2023......
  • springboot2+mybatis+shardingsphere-5.5.1
    注意:1.druid不能boot-starter方式引入2.snakeyaml需要1.33('voidorg.yaml.snakeyaml.LoaderOptions.setCodePointLimit(int)') #303183.spring.datasource.driverClassName:org.apache.shardingsphere.driver.ShardingSphereDriver4.如果使用了quartz,需要指定独立数据源(Tabl......
  • javaWeb开发实战:spring MVC+MyBatis实现网页登录验证
    1.环境和工具Idea2019、Tomcat8、Jdk82.新建springMVC项目打开idea,新建项目,选择springMVC->next:填写项目名、路径->finish完成创建3.项目属性配置文件(file)->项目结构:检查sdk、模块设置是否正确。4.运行调试配置Addconfigration点击“+”号,选择tomcat->loca......
  • mybatis-generator使用
    Mybatis-generator使用一.添加依赖 <!--首先要有mybatis的依赖和数据库驱动--> <dependencies> <!--mybatis依赖--><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-......
  • Mybatis的基本应用
    一框架简介1.1三层架构软件开发常用的架构是三层架构,之所以流行是因为有着清晰的任务划分。一般包括以下三层:持久层:主要完成与数据库相关的操作,即对数据库的增删改查。                      因为数据库访问的对象一般称为Da......
  • SQL注入攻击及其在SpringBoot中使用MyBatisPlus的防范策略
    SQL注入攻击及其在SpringBoot中使用MyBatisPlus的防范策略随着互联网技术的飞速发展,Web应用的安全问题日益凸显,其中SQL注入攻击是最常见的安全威胁之一。SQL注入攻击不仅可能导致敏感数据泄露,还可能引发数据篡改、服务中断等严重后果。本文将详细介绍SQL注入攻击的基本概念......
  • Mybatis的学习
    这次学习了Mybatis的用法,并且跟着视频做了实际应用1、创建user表,添加数据2、创建Maven项目模块,导入坐标4.0.0<groupId>org.example</groupId><artifactId>mybites-demo</artifactId><version>1.0-SNAPSHOT</version><properties><maven.compiler.sour......
  • MyBatis项目的创建和增删查改操作
    1.项目的创建第一步还是和之前的Spring项目一样然后还需要添加以下依赖,Lombok和web依赖也可以加上之后需要进行数据库的配置,这里通过yml来演示:spring:application:name:mybatis-demodatasource:url:jdbc:mysql://127.0.0.1:3306/mybatis_tes......
  • 【MyBatis源码】SQL 语句构建器AbstractSQL
    文章目录介绍org.apache.ibatis.jdbc.SQLSQL类使用示例@SelectProvider搭配动态SQLAbstractSQL类源码分析介绍当我们需要使用Statement对象执行SQL时,SQL语句会嵌入Java代码中。SQL语句比较复杂时,我们可能会在代码中对SQL语句进行拼接,查询条件不固定时,还需要根据不同......