一、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、特殊字符处理
符号 实体
<:<
>:>
&:&
':'
":"
六、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() < 5"> <bind name="likeUsername" value="'%'+username+'%'"></bind> username like #{likeUsername} </when> <when test="username.length() < 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