Mybatis 从入门到入魔
Mybatis
1.入门
1.创建工程,引入坐标
<dependencies> <dependency> <groupId>org.mybatis</groupId> <ar tifactId>mybatis</artifactId> <version>3.5.0</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> </dependency> <dependency><groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.12</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency>
</dependencies>
2.配置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"> <configuration>
<!--指定默认的环境-->
<environments default="mysql"><!--环境配置,可以存在多个--> <environment id="mysql"> <!--使用了JDBC的事务管理--> <transactionManager type="JDBC"></transactionManager> <!--先配置为POOLED,代表以池的形式管理连接--> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://127.0.0.1:3306/es" /> <property name="username" value="root"/> <property name="password" value="adminadmin"/>
</dataSource>
</environment>
</environments>
</configuration>
3.xml映射文件
文件位置:\resources\com\itheima\mapper\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="userMapper"> <insert id="save" parameterType="com.itheima.domain.User"> INSERT INTO USER(NAME,PASSWORD,email,phoneNumber,birthday) VALUE(#{name},#{password},#{email},#{phoneNumber},#{birthday}); </insert>
</mapper>
4.将Mapp文件加入到主配置文件中
<?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"><!--省略数据库配置--> </environments> <!--引入映射文件--> <mappers> <mapper resource="com/itheima/mapper/UserMapper.xml" /> </mappers>
</configuration>
5.测试
//保存
@Test
public void testSave() throws Exception {
User user = new User(); user.setName("传智播客");
user.setPassword("admin"); user.setBirthday(new Date());
user.setEmail("admin@itcast.cn"); user.setPhoneNumber("110");//1 加载Mybatis的主配置文件 InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml"); //2 创建sqlSessionFactory工厂SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //3 创建sqlSessionSqlSession sqlSession = sqlSessionFactory.openSession(); //4.执行操作 sqlSession.insert("userMapper.save",user); //5 提交事务 sqlSession.commit(); //6 释放资源 sqlSession.close();}
小结:
基于接口代理方式的开发只需要程序员编写 Mapper 接口,Mybatis 框架会为我们动态生成实现类的对象。
这种开发方式要求我们遵循一定的规范:
- Mapper接口的类路径与Mapper.xml 文件中的namespace相同
- Mapper接口方法名和Mapper.xml中定义的每个statement的id相同
- Mapper接口方法的输入参数类型和Mapper.xml中定义的每个sql的parameterType的类型相同
- Mapper接口方法的输出参数类型和Mapper.xml中定义的每个sql的resultType的类型相同
2.Mybatis基于接口代理方式的内部执行原理
我们的Dao层现在只有一个接口,而接口是不实际干活的,那么是谁在做save的实际工作呢?
下面通过追踪源码看一下:
1、通过追踪源码我们会发现,我们使用的mapper实际上是一个代理对象,是由MapperProxy代理产生的。
2、追踪MapperProxy的invoke方法会发现,其最终调用了mapperMethod.execute(sqlSession, args)
3、进入execute方法会发现,最终工作的还是sqlSession。
3.Mybatis基本原理:
4.Mybatis的API
Resources加载mybatis的配置文件。SqlSessionFactoryBuilder利用Resources指定的资源,将配置信息加载到内存中,还会加载mybatis配置文件中指定的所有映射配置信息, 并用特定的对象实例进行保存,从而创建SqlSessionFactory对象。SqlSessionFactory这是一个工厂对象,对于这种创建和销毁都非常耗费资源的重量级对象,一个项目中只需要存在一个即可。也就是说,它的生命周期跟项目的生命周期是一致的(项目不死,我不销毁)它的任务是创建SqlSession。SqlSession这是Mybatis的一个核心对象。我们基于这个对象可以实现对数据的CRUD操作。对于这个对象应做到每个线程独有,每次用时打开,用完关闭。
抽取工具类
- 抽取基本代码工具类
public class MybatisUtil {private static SqlSessionFactory sqlSessionFactory = null;static {try {InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);} catch (Exception e) {throw new RuntimeException("加载配置文件失败");}}public static SqlSession openSession() {return sqlSessionFactory.openSession();} }
2 抽取测试基类
public class BaseMapperUtil {protected SqlSession sqlSession = null;@Beforepublic void init() {sqlSession = MybatisUtil.openSession();}@Afterpublic void destory() {sqlSession.commit();sqlSession.close();}
}
5.基于接口代理实现CRUD
//Mapper接口public interface UserMapper {//保存void save(User user);//根据UID查询User findByUid(Integer i);//根据UID更新void update(User user);//根据ID删除void deleteByUid(Integer uid);}
//Mapper.xml
<mapper namespace="com.itheima.mapper.UserMapper"><insert id="save" parameterType="com.itheima.domain.User">insert intouser(name,password,email,phoneNumber,birthday)value(#{name},#{password},#{email},#{phoneNumber},#{birthday})</insert><select id="findByUid" parameterType="int" resultType="com.itheima.domain.User">select * from user where uid = #{id}</select><update id="update" parameterType="com.itheima.domain.User">update user set password = #{password} where uid = #{uid}</update><update id="deleteByUid" parameterType="int">delete from user where uid = #{uid}</update>
</mapper>
//测试类public class CRUDTest extends BaseMapperUtil {//保存@Testpublic void testSave() throws Exception {User user = new User();user.setName("传智播客3");user.setPassword("admin");user.setBirthday(new Date());user.setEmail("admin@itcast.cn");user.setPhoneNumber("110");UserMapper userMapper = sqlSession.getMapper(UserMapper.class);userMapper.save(user);}//根据UID查询@Testpublic void testFindByUid() {UserMapper userMapper = sqlSession.getMapper(UserMapper.class);User user = userMapper.findByUid(1);System.out.println(user);}//根据UID修改@Testpublic void testUpdate() {User user = new User();user.setUid(1);user.setPassword("adminadmin");UserMapper userMapper = sqlSession.getMapper(UserMapper.class);userMapper.update(user);}//根据ID删除@Testpublic void testDeleteByUid() {UserMapper userMapper = sqlSession.getMapper(UserMapper.class);userMapper.deleteByUid(2);}}
5.1查询
<!--当仅仅有一个查询条件的时候parameterType="" 可以省略#{} 占位符可以随便写,只要不为空可以【因为参数只要一个,所以名字就不重要了,但是推荐跟名称一致】
-->
<select id="findByEmail" resultType="com.itheima.domain.User">select * from user where email = #{emailFFFFFFFFFF}
</select>
多个条件
第一种方式:不使用注解,这时候xml中的SQL语句中只能使用arg 或者param 获取参数//根据多个条件查询List<User> findByEmailAndphoneNumber(String email, String phoneNumber);<select id="findByEmailAndphoneNumber" resultType="com.itheima.domain.User"><!--select * from user where email = #{arg0} and phoneNumber = #{arg1}-->select * from user where email = #{param1} and phoneNumber = #{param2}</select>
第二种方式:使用注解,引入@Param()注解,这时候就可以是用属性名称获取参数了//根据多个条件查询List<User> findByEmailAndphoneNumber(@Param("email") String email, @Param("phoneNumber")String phoneNumber);<select id="findByEmailAndphoneNumber" resultType="com.itheima.domain.User">select * from user where email = #{email} and phoneNumber = #{phoneNumber}</select>
第三种方式(推荐使用):使用pojo对象传递参数//使用对象封装查询条件List<User> findByEmailAndphoneNumber2(User user);<select id="findByEmailAndphoneNumber2" resultType="com.itheima.domain.User">select * from user where email = #{email} and phoneNumber = #{phoneNumber}</select>
5.2模糊查询
<!--方式一: 传入参数的时候,直接传入setEmail(%传智%)-->
<select id="findByName1" resultType="com.itheima.domain.User">select * from user where name like #{email}
</select><!--方式二: 在SQL语句中直接控制模糊模式'%' 传入参数的时候,直接传入setEmail(传智)-->
<select id="findByName2" resultType="com.itheima.domain.User">select * from user where name like "%"#{email}"%"
</select><!--方式三: 使用${}赋值,注意$不表示占位符,而是表示字符串拼接,而且要求接口方法中要使用@Param声明-->
<select id="findByName3" resultType="com.itheima.domain.User">select * from user where name like "%${email}%"
</select><!--方式四: 使用MySQL的函数, 推荐!!!-->
<select id="findByName4" resultType="com.itheima.domain.User">select * from user where name like concat('%',#{email},'%');
</select>
面试直达:请说出Mybatis中#{}和${}的区别
#{}表示占位符,${}表示字符串拼接
${}可能会引起SQL注入问题,#{}不会
两者都可以接受简单类型的值和pojo类型的属性值,但是${}接收简单类型数据只能使用${value},#{} 可以是随意值
6.返回主键
6.1 useGeneratedKeys
使用useGeneratedKeys="true" 声明返回主键
使用keyProperty指定将主键映射到pojo的哪个属性
<insert id="saveReturnId" useGeneratedKeys="true" keyProperty="uid">insert intouser(name,password,email,phoneNumber,birthday)value(#{name},#{password},#{email},#{phoneNumber},#{birthday})
</insert>
6.2 selectKey
<insert id="saveReturnId"><!--keyColumn 指定主键列名称keyProperty 指定主键封装到实体的哪个属性resultType 指定主键类型order 指定在数据插入数据库前(后),执行此语句--><selectKey keyProperty="uid" keyColumn="uid" resultType="int" order="AFTER">select last_insert_id()</selectKey>insert intouser(name,password,email,phoneNumber,birthday)value(#{name},#{password},#{email},#{phoneNumber},#{birthday})
</insert>
7.动态SQL
根据程序运行时传入参数的不同而产生SQL语句结构不同,就是动态SQL。
findByCondtion(User user):根据传入的user对象进行查询,将不为空的属性作为查询条件
用户输入的是: 用户名和密码 select * from user where name= #{name} and password = #{password}
用户输入的是: 用户名和邮箱select * from user where name= #{name} and email = #{email}
用户输入的是: 用户名和和密码和邮箱select * from user where name= #{name} and password = #{password} and email = #{email}
动态SQL是Mybatis的强大特性之一,Mybatis3之后,需要了解的动态SQL标签仅仅只有下面几个:
- if choose (when, otherwise) 用于条件判断
- trim (where, set) 用于去除分隔符
- foreach 用于循环遍历
7.1 条件判断
7.1.1 if
if 用来做条件判断,接受一个ognl表达式,返回一个boolean值。< if test="ognl表达式">sql片段< /if>。
第一种情况: if 在where语句中的使用,实现动态条件判断
需求:根据传入的user对象的进行查询,将不为空的属性作为查询条件
//实现根据传入的值是否为空实现where语句拼接
//注意语句中使用了 1=1 来避免语句出现错误
<select id="findByCondition" resultType="com.itheima.domain.User">select * from user where 1=1<if test="name != null and name != ''">and name like concat('%',#{name},'%')</if><if test="email != null and email != ''">and email = #{email}</if>
</select>
第二种情况: if 在set语句中的使用,实现动态条件更新
需求:根据传入的user对象的进行更新,将不为空的属性更新到数据库
//实现根据传入的值是否为空实现set语句拼接
//注意语句中使用了 uid = #{uid} 来避免语句出现错误
<update id="update">update user set<if test="name != null and name != ''">name = #{name},</if><if test="email != null and email != ''">email = #{email},</if>uid = #{uid} where uid = #{uid}
</update>
第三种情况: if 在insert语句中的使用,实现动态条件插入
需求:根据传入的user对象的进行插入,将不为空的属性插入到数据库
//实现根据传入的值是否为空实现insert语句拼接
<insert id="insert">insert intouser(name,email,phoneNumber,birthday<if test="password != null and password != ''">,password</if>)value(#{name},#{email},#{phoneNumber},#{birthday}<if test="password != null and password != ''">,#{password}</if>)
</insert>
7.1.2 choose、when、otherwise
有时候,我们不想用到所有的查询条件,只想选择其中的一个,查询条件有一个满足即可,使用 choose 标签可以解决此类问题,类似于 Java 的if elseif 语句。
<select id="select" resultType="com.itheima.domain.User">select * from user where 1=1<choose><when test="name != null and name != ''">and name = #{name}</when><when test="email != null and email != ''">and email = #{email}</when><when test="phoneNumber != null and phoneNumber != ''">and phoneNumber = #{phoneNumber}</when><otherwise>and 1 = 2</otherwise></choose>
</select>
下面是java和mybatis中的判断语句语法的对比
7.2 格式优化
这三个关键字是用来对Mybatis中的SQL语句书写进行优化的。主要是用来去除多余的符号和关键字。
7.2.1 where
where标签的作用:
如果where包含的标签中有返回true的语句,Mybatis会在语句插入一个‘where’。
如果标签返回的内容是以AND 或OR 开头的,Mybatis会将其剔除掉。
select * from user and name like concat('%',#{name},'%') and email = #{email}
7.2.2 set
set标签的作用:去掉set语句最后的逗号(,)
注意:set标签中必须保证至少有一个语句
<update id="update2">update user<set><if test="name != null and name != ''">name = #{name},</if><if test="email != null and email != ''">email = #{email},</if>uid = #{uid}</set>where uid = #{uid}
</update>
7.2.3 trim(了解)
trim标签是一个格式化标签,可以完成set或者是where的功能。
//使用trim替代where
//prefix 代表必要的时候在trim标签的最前面加上一个where
//prefixOverrides 代表必要的时候干掉trim生成语句中的第一个and或者or
<select id="find3" resultType="com.itheima.domain.User">select * from user<trim prefix="where" prefixOverrides="and | or"><if test="name != null and name != ''">and name like concat('%',#{name},'%')</if><if test="email != null and email != ''">and email = #{email}</if></trim>
</select>//使用trim替代set
//prefix 代表必要的时候在trim标签的最前面加上一个set
//suffixOverrides 代表必要的时候干掉trim生成语句中的最后一个,
<update id="update3">update user<trim prefix="set" suffixOverrides=","><if test="name != null and name != ''">name = #{name},</if><if test="email != null and email != ''">email = #{email},</if></trim>where uid = #{uid}
</update>
8 循环遍历
foreach主要是用来做数据的循环遍历。
典型的应用场景是SQL中的in语法中,select * from user where uid in (1,2,3) 在这样的语句中,传入的参数部分必须依靠 foreach遍历才能实现。我们传入的参数,一般有下面几个形式:
集合(List Set)
数组
pojo
foreach的选项
collection:数据源【重点关注这一项,它的值会根据出入的参数类型不同而不同】
open:开始遍历之前的拼接字符串
close:结束遍历之后的拼接字符串
separator:每次遍历之间的分隔符
item:每次遍历出的数据
index:遍历的次数,从0开始
8.1 集合
<select id="find4" resultType="com.itheima.domain.User"><!--collection:数据源【当参数为一个集合时,此值为collection,如果参数是List,此值也可以使用list代替】此处的值也可以通过@Param("自定义")的方式在Dao层方法上自己指定。--><!--select * from user where uid in (1,2,3)-->select * from user where uid in<foreach collection="collection" open="(" close=")" separator="," item="item">#{item}</foreach>
</select>List<User> find4(List<Integer> uids);//collection="collection"
//或者
List<User> find4(@Param("uids") List<Integer> uids);//collection="uids"
8.2数组
<select id="find5" resultType="com.itheima.domain.User"><!--collection:数据源【当参数为一个数组时,此值为array】此处的值也可以通过@Param("自定义")的方式在Dao层方法上自己指定--><!--select * from user where uid in (1,2,3)-->select * from user where uid in<foreach collection="array" open="(" close=")" separator="," item="item">#{item}</foreach>
</select>List<User> find5(Integer[] uids);//collection="array"
//或者
List<User> find5(@Param("uids") Integer[] uids);//collection="uids"
8.3 pojo
<select id="find6" resultType="com.itheima.domain.User">select * from user where uid in<foreach collection="uids" open="(" close=")" separator="," item="item" index="index">#{item}</foreach>
</select>List<User> find6(User user);//在user中封装了一个List<Integer> uids属性
总结:foreach的使用主要是用来遍历数据源。关键点在于数据源的指定:
- 如果是集合,使用collection
- 如果是数组,使用array
- 如果是pojo,使用pojo中的属性名称
小结
动态SQL的最终目的其实就是通过Mybatis提供的标签实现sql语句的动态拼装。
为了保证拼接准确,我们最好首先要写原生的sql语句出来,然后再通过mybatis动态sql对照着改。
9.多表关系
9.1 多表关系分析
数据库设计的三种表间关系分别为: 一对一 、一对多(多对一)、 多对多关系。
常见示例:一对一:人和身份证、QQ号码和QQ详情一对多:用户和登录日志、部门和员工多对多:用户和角色、老师和学生
明确:我们今天只涉及实际开发中常用的关联关系,一对多和多对多。而一对一的情况,在实际开发中几乎不用。
9.2 多对一
多对一的映射方式一般有下面几种:
- 采用外键+别名的形式进行映射
- 采用resultMap形式进行映射
- 采用resultMap + association 形式进行映射 【推荐】
下面分别来看。
9.2.1 采用外键+别名的形式进行映射
<select id="findAll" resultType="com.itheima.domain.LoginInfo"><!--select * from user u,login_info l where u.uid = l.uid;-->selectl.*,u.uid "user.uid",u.name "user.name",u.password "user.password",u.email "user.email",u.phoneNumber "user.phoneNumber",u.birthday "user.birthday"from user u,login_info l where u.uid = l.uid
</select>
9.2.2 采用resultMap形式进行映射
//注意此方式还支持继承
<resultMap id="loginInfoMap" type="com.itheima.domain.LoginInfo"><id property="lid" column="lid" /><result property="ip" column="ip" /><result property="loginTime" column="loginTime" /><result property="user.uid" column="uid" /><result property="user.name" column="name" /><result property="user.password" column="password" /><result property="user.email" column="email" /><result property="user.phoneNumber" column="phoneNumber" /><result property="user.birthday" column="birthday" />
</resultMap><select id="findAll2" resultMap="loginInfoMap">select * from user u,login_info l where u.uid = l.uid;
</select>
9.2.3 采用resultMap + association 形式进行映射
<resultMap id="loginInfoMap3" type="com.itheima.domain.LoginInfo"><id property="lid" column="lid" /><result property="ip" column="ip" /><result property="loginTime" column="loginTime" />//property代表属性名称 javaType对应真实的类型 <association property="user" javaType="com.itheima.domain.User"><result property="uid" column="uid" /><result property="name" column="name" /><result property="password" column="password" /><result property="email" column="email" /><result property="phoneNumber" column="phoneNumber" /><result property="birthday" column="birthday" /></association>
</resultMap><select id="findAll3" resultMap="loginInfoMap3">select * from user u,login_info l where u.uid = l.uid;
</select>
9.3 一对多
一对多的映射方式一般采用resultMap + collection形式进行实现。
<resultMap id="userMap2" extends="userMap" type="com.itheima.domain.User"><id property="uid" column="uid"/><result property="name" column="name"/><result property="password" column="pass"/><result property="email" column="email"/><result property="phoneNumber" column="phoneNumber"/><result property="birthday" column="birthday"/>//loginInfos配置属性 ofType配置指定的类<collection property="loginInfos" ofType="com.itheima.domain.LoginInfo"><id property="lid" column="lid" /><result property="ip" column="ip" /><result property="loginTime" column="loginTime" /></collection>
</resultMap><select id="findAll" resultMap="userMap2">select * from user u left join login_info l on u.uid = l.uid;
</select>
9.4 多对多
多对多的配置跟一对多很相似,难度在于SQL语句的编写。
<resultMap id="userMap3" type="com.itheima.domain.User"><id property="uid" column="uid"/><result property="name" column="name"/><result property="password" column="pass"/><result property="email" column="email"/><result property="phoneNumber" column="phoneNumber"/><result property="birthday" column="birthday"/><collection property="roles" ofType="com.itheima.domain.Role"><id property="rid" column="rid" /><result property="name" column="name" /><result property="description" column="description" /></collection>
</resultMap><select id="findAll3" resultMap="userMap3">SELECT * FROM USER u LEFT JOIN user_role ur ON u.uid = ur.uid JOIN role r ON ur.`rid` = r.`rid`
</select>
10.嵌套查询
10.1 什么是嵌套查询?
嵌套查询就是将原来多表查询中的联合查询语句拆成单个表的查询,再使用mybatis的语法嵌套在一起。
举个例子:需求: 查询日志的同时查询到用户信息联合查询: select * from login_info l,user u where l.uid = u.uid 改成嵌套查询: 1) 先查询到日志信息(这里面包含了一个跟用户相关的外键uid) select * from login_info 得到uid2)根据上一拿到的uid查询相关的用户信息select * from user where uid = #{第一步得到的uid}3)使用Mybatis提供的关键字,将上面两步嵌套起来。。。。
10.2 多对一的嵌套查询
目标: 查询日志的同时查询到用户信息
未使用嵌套之前:
<resultMap id="baseMap" type="com.itheima.domain.LoginInfo"><id property="lid" column="lid" /><result property="ip" column="ip" /><result property="loginTime" column="loginTime" />
</resultMap><resultMap id="loginInfoMap1" type="com.itheima.domain.LoginInfo" extends="baseMap"><association property="user" javaType="com.itheima.domain.User"><id property="uid" column="uid" /><result property="name" column="name" /><result property="password" column="password" /><result property="email" column="email" /><result property="phoneNumber" column="phoneNumber" /><result property="birthday" column="birthday" /></association>
</resultMap><select id="findAll1" resultMap="loginInfoMap1">select * from login_info l,user u where l.uid = u.uid
</select>
改成嵌套查询:
- 对日志表进行单表查询
<!-- LoginInfoMapper.xml --><resultMap id="loginInfoMap2" type="com.itheima.domain.LoginInfo" extends="baseMap"><association><!--此处暂时留空--></association></resultMap><select id="findAll2" resultMap="loginInfoMap2">select * from login_info</select>
- 在用户表的Mapper文件中:使用uid查询用户
<!--UserMapper.xml--><select id="findByUid" resultType="com.itheima.domain.User">select * from user where uid = #{uid}</select>
- 使用Mybatis提供的关键字,将上面两步嵌套起来
<!--在LoginInfoMapper.xml中进行嵌套关联--><resultMap id="loginInfoMap2" type="com.itheima.domain.LoginInfo" extends="baseMap"><!--select 指定联合查询的方法(根据uid查询User)column 要传递的参数属性字段(将此字段的值作为参数传给select方法)--><association property="user" column="uid"select="com.itheima.mapper.UserMapper.findByUid"></association></resultMap>
10.3 一对多的嵌套查询
目标: 查询用户的同时查询到关联的日志信息未使用嵌套之前:
<resultMap id="baseMap" type="com.itheima.domain.User"><id property="uid" column="uid"/><result property="name" column="name"/><result property="password" column="password"/><result property="email" column="email"/><result property="phoneNumber" column="phoneNumber"/><result property="birthday" column="birthday"/>
</resultMap><resultMap id="userMap1" type="com.itheima.domain.User" extends="baseMap"><collection property="loginInfos" ofType="com.itheima.domain.LoginInfo"><id property="lid" column="lid"/><result property="ip" column="ip"/><result property="loginTime" column="loginTime"/></collection>
</resultMap><select id="findAll1" resultMap="userMap1">select * from user u left join login_info l on u.uid = l.uid;
</select>
改成嵌套查询:
- 对用户表进行单表查询
<!--UserMapper.xml--><resultMap id="userMap2" type="com.itheima.domain.User" extends="baseMap"><collection><!--此处暂时留空--></collection></resultMap><select id="findAll2" resultMap="userMap2">select * from user</select>
- 在日志表的Mapper文件中:使用uid查询日志
<!--LoginInfoMapper.xml--><select id="findByUid" resultType="com.itheima.domain.LoginInfo">select * from login_info where uid = #{uid}</select>
- 使用Mybatis提供的关键字,将上面两步嵌套起来
<!--UserMapper.xml--><resultMap id="userMap2" type="com.itheima.domain.User" extends="baseMap"><collection property="loginInfos" column="uid" select="com.itheima.mapper.LoginInfoMapper.findByUid"></collection></resultMap>
11.加载策略
11.1 什么是加载策略
当多个模型之间存在联系时,在加载一个模型的数据的时候,是否随之加载与其相关模型数据的策略,我们称之为加载策略。
在Mybatis中,常用的加载策略有立即加载和延迟加载(懒加载)两种。
举个例子:现在有用户和用户登录日志两个模型,当加载1个用户的时候,是否需要立即加载与其相关的登录日志的信息呢?
如果需要,我们把这种加载策略成为立即加载,
如果不需要,等到真正要使用日志信息的时候再加载,我们把这种加载策略成为立即加载(懒加载)。
11.2 Mybatis加载策略
Mybatis的默认加载策略是立即加载,也就是在加载一个对象的时候会立即联合加载到其关联的对象。
当然,Mybatis也提供了修改加载策略的方法。
- 在Mybatis 的配置文件中可以使用setting修改全局的加载策略。
- 在< association >和< collection >元素中都有一个fetchType属性,该值会覆盖掉全局参数的配置。
fetchType=“lazy” 懒加载策略
fetchType=“eager” 立即加载策略
<association fetchType="eager|lazy"></association> <collection fetchType="eager|lazy"></collection>
注意:
- 在配置了延迟加载策略后,即使没有调用关联对象的任何方法,当你调用当前对象的equals、clone、hashCode、toString方法时也会触发关联对象的查询。
- 在配置文件中可以使用lazyLoadTriggerMethods配置项覆盖掉mybatis的默认行为。
<setting name="lazyLoadTriggerMethods" value="getUid,toString"/>
12 缓存机制
缓存是用来提高查询效率的,所有的持久层框架基本上都有缓存机制。
Mybatis有两级缓存,一级缓存是SqlSession级别的,二级缓存是SqlSessionFactory级别的。
12.1 一级缓存
一级缓存是SqlSession级别的缓存,是默认开启且无法关闭的。
public void testFindByUid(){UserMapper mapper = sqlSession.getMapper(UserMapper.class);User user1 = mapper.findByUid(1);//发送SQLSystem.out.println("==============");User user2 = mapper.findByUid(1);//未发送SQLSystem.out.println(user1 == user2);//true
}
- 同一个sqlSession中两次执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。
- 当一个sqlSession结束后该sqlSession中的一级缓存也就不存在了。
- 不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。
注意:
- 调用SqlSession的clearCache(),或者执行C(增加)U(更新)D(删除)操作,都会清空缓存。
- 查询语句中这样的配置< select flushCache=“true”/>也会清除缓存。
12.2 二级缓存
二级缓存是sqlSessionFactory级别的缓存,是默认开启,但是可以关闭的。
二级缓存是多个SqlSession共享的,不同的sqlSession两次执行相同namespace下的sql语句,第一次执行完毕会将数据库中查询的数据写到二级缓存(内存),第二次会从二级缓存中获取数据,而不再从数据库查询,从而提高查询效率。
二级缓存的设置:
<!--主配置文件中配置cacheEnable为true,这也是默认配置,可以不加-->
<setting name="cacheEnabled" value="true|false"/><!--在Mapper.xml文件中加入cache标签 -->
<cache />
验证:
public void testFindByUid2(){SqlSession sqlSession1 = MybatisUtil.openSession();User user1 = sqlSession1.getMapper(UserMapper.class).findByUid(1);//发送SQLsqlSession1.close();System.out.println("===================");SqlSession sqlSession2 = MybatisUtil.openSession();User user2 = sqlSession2.getMapper(UserMapper.class).findByUid(1);//不发送SQLsqlSession2.close();//注意这里得到的对象是从缓存中拷贝出来的,如果直接使用一个会有线程安全问题System.out.println(user1 == user2);//false
}
13.配置文件
Mybatis的配置文件中有很多可配置项,我们只研究几个常用的。
注意:配置文件中的各个配置项要严格按照dtd中规定的顺序书写,可以省略某些项,但是不能颠倒顺序。
13.1 properties:引入外部配置文件
用来引入外部的配置。
我们经常把一些敏感信息单独放在一个文件中,然后通过properties 引入到配置文件。
举个例子:我们跟数据库相关的信息单独配置
1)创建一个db.properties存放相关信息。
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/es
jdbc.username=root
jdbc.password=adminadmin
2)在mybatis的配置文件中引入配置文件
<!--还支持使用<properties url="">的方式引入网络上的配置文件--><properties resource="db.properties" />
3 ) 在需要的地方使用el表达式引入需要的值
<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>
13.2 typeAlias:配置别名
mybatis支持别名机制。所谓别名,就是给比较长的名字起一个比较短的名字,在使用的时候二者等价而已。
默认支持的别名
自定义别名
mybatis的配置文件中有一个typeAliases标签,在这个标签下可以自定义别名。
- 单个定义别名
- 使用包的形式批量定义别名
当定义完别名后,下面两种使用方式就是等价的。
<select id="findByUid" resultType="com.itheima.domain.LoginInfo"></select>
<select id="findByUid" resultType="loginInfo"></select>
13.3 mappers:注册映射配置
对于将我们的Mapper文件注册到主配置文件,mybatis支持三种方式:
<!--1、直接注册Mapper映射文件-->
<mapper resource="com/itheima/dao/UserMapper.xml"/><!--2、直接注册Mapper接口类-->
<mapper class="com.itheima.dao.UserMapper"/><!--3、注册mapper包,包下的配置文件会全部被注册-->
<package name="com.itheima.mapper"/>
13.4 setting:一些常用配置
<settings><!--开启懒加载,默认是false--><setting name="lazyLoadingEnabled" value="true"/><!--覆盖默认的导致懒加载失效的方法--><setting name="lazyLoadTriggerMethods" value=""/>
</settings>
13.5 transactionManager:事务控制
Mybatis底层使用的是JDBC的事务管理,只不过在上面封装了一下。
<transactionManager type="JDBC"></transactionManager>
在JDBC中我们可以通过调用Connection的setAutoCommit()方法来开关对事务的支持。
true代表自动提交事务(默认),false代表需要手动调用commit()\rollback()等事务控制方法。mybatis框架是对JDBC的封装,底层也是调用Connection的setAutoCommit()方法来控制事务的。
只不过,Mybatis在JDBC的基础上做了修改,默认手动提交事务。//Mybatis的事务控制很简单主要是下面三个API
SqlSession sqlSession = SqlSessionFactory.openSession(true);//开启事务(此事务自动提交)
SqlSession sqlSession = SqlSessionFactory.openSession();//提交事务(此事务需要手动提交)SqlSession.commit();//提交事务
SqlSession.rollback();//回滚事务
13.6 dataSource: 数据源
<dataSource type="POOLED"><!--这里的name都是数据源声明好的,必须按照这个写,每一种数据源都应该提供这些基本的参数接受项--><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>mybatis中使用了标准的 JDBC 数据源(javax.sql.DataSource)接口来配置 JDBC 连接对象,可以通过mybatis的配置文件dataSource的type属性来指定。可配置项有三个:
- UNPOOLED:不使用连接池的数据源。这个数据源的实现只是每次被请求时打开和关闭连接。
- POOLED:使用连接池的数据源。这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。它继承并扩展了UNPOOLED,增加了连接池的功能。
- JNDI:不做了解,知道就行。
思考:如何来更换连接池呢?比如我们想druid连接池,怎么办?
步骤:
引入druid坐标
com.alibaba druid 1.1.15
2)自定义一个工厂类继承UnpooledDataSourceFactory,在构造方法中返回DruidDataSource数据源
public class DruidDataSourceFactory extends UnpooledDataSourceFactory {public DruidDataSourceFactory() {this.dataSource = new DruidDataSource();}
}
3)修改配置文件
<!--这里的type其实就是自定义产生数据源的工厂-->
<dataSource type="com.itheima.datasource.DruidDataSourceFactory"><!--这里的name的值要根据DruidDataSource规定的方式写--><property name="driverClass" value="${jdbc.driver}"/><property name="jdbcUrl" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/>
</dataSource>
使用注解书写SQL:
Mybatis除了支持在xml中书写SQL,也支持使用注解的形式编写SQL。
但这样的形式只适合书写非常简单sql语句。
public interface UserMapper {@Insert("insert into user(name) values(#{name})")public void insertT(User user);@Delete("delete from user where id=#{id}")public void deleteById(int id);@Update("update user set name=#{name} where id=#{id}")public void updateT(User user);@Select("select * from user where id=#{id}")public User getUserById(int id);@Select("select * from user")public List<User> getAll();
}
- 使用注解不能完成动态SQL
- 如果SQL非常复杂,那么使用注解写在java文件中,反而感觉不伦不类
- 硬编码在java文件中,如果需要修改SQL语句,必须要重新编译。
Mybatis 从入门到入魔相关推荐
- c++框架有哪些_Java Mybatis框架入门教程_v20200726
MyBatis 的前身是 Apache 的开源项目 iBatis.MyBatis 几乎可以代替 JDBC,是一个支持普通 SQL 查询,存储过程和高级映射的基于 Java 的优秀持久层框架.MyBat ...
- MyBatis学习总结(1)——MyBatis快速入门
2019独角兽企业重金招聘Python工程师标准>>> 一.Mybatis介绍 MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架.MyBatis消除了几乎所 ...
- MyBatis基础入门《九》ResultMap自动匹配
MyBatis基础入门<九>ResultMap自动匹配 描述: Mybatis执行select查询后,使用ResultMap接收查询的数据结果. 实体类:TblClient.java 接口 ...
- mybatis的入门
mybatis的环境搭建 第一步:创建maven工程并导入坐标 第二步:创建实体类和dao的接口 第三步:创建Mybatis的主配置文件 ...
- Mybatis最入门---代码自动生成(generatorConfig.xml配置)
[一步是咫尺,一步即天涯] 经过前文的叙述,各位看官是不是已经被Mybatis的强大功能给折服了呢?本文我们将介绍一个能够极大提升我们开发效率的插件:即代码自动生成.这里的代码自动生成包括,与数据库一 ...
- mybatis select count(*) 一直返回0 mysql_Mybatis教程1:MyBatis快速入门
点击上方"Java技术前线",选择"置顶或者星标" 与你一起成长 一.Mybatis介绍 MyBatis是一个支持普通*SQL*查询,存储过程和高级映射的优秀持 ...
- MyBatis学习笔记(一)——MyBatis快速入门
转自孤傲苍狼的博客:http://www.cnblogs.com/xdp-gacl/p/4261895.html 一.Mybatis介绍 MyBatis是一个支持普通SQL查询,存储过程和高级映射的优 ...
- Mybatis从入门到精通下篇
Mybatis从入门到精通下篇: 输入类型: 输出类型: ResultMap: 动态sql: if标签: where标签: sql片段: foreach标签: 关联查询: 以订单作为主体: 一对一查询 ...
- Java基础-SSM之mybatis快速入门篇
Java基础-SSM之mybatis快速入门篇 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 其实你可能会问什么是SSM,简单的说就是spring mvc + Spring + m ...
- Mybatis的入门到精通这一篇文章就够了
Mybatis的产生 idea 的配置 idea当中配置好maven工具 1Idea当中配置自动编译 Mybatis是什么? Mybatis的执行原理图 Mybatis的入门Demo 对象的详解 Re ...
最新文章
- html style属性
- 《超越需求:敏捷思维模式下的分析》—第1章 1.2节交付价值
- java code viewer_Java CodeView类代码示例
- 闭包---在函数内部再定义一个函数
- 测试人员要了解的知识
- 彻底理解 Cookie、Session、Token
- spring4.0:@Configuration的使用
- C罗捧得史上首个区块链得分王奖杯 1600名支付宝用户获“同款”
- CCF认证2014-9-2 画图
- 2006吴山庙会-怎么都是人啊?
- 软考高级 真题 2013年下半年 信息系统项目管理师 综合知识
- 为SM30视图创建TCODE
- 品牌出海:如何做好本土化运营?
- 面试小纸条(MySQL第一弹)
- 微信兔子,比较下来算是比较好用的工具
- EDA12--DC脚本命令(一)
- 支持向量机检测DGA
- [NLP --- 3] 文档检索算法TF-IDF
- 计算机技术变化太快,这世界变化太快!Ps修图进入“智能时代”!
- ReportViewer在Chrome 浏览器中无法显示的解决方法
热门文章
- PAT Basic 1031
- 青少年Python编程
- 计算机内存有何组成,电脑4个4g内存条组成16g内存和16g内存有什么不同?
- Navicat使用总结(2022.9)
- led灯光衰怎么解决_影响LED灯具光衰的原因及解决方法
- python获取第一个字符_Python:获取列表中第一个字符串的第一个字符?
- mysql audit log_关于MySQL AUDIT(审计)那点事
- 域名查询服务商的方法
- 大话西游免费版最新服务器是,2020年4月1日服务器数据互通公告
- 千峰JAVA逆战班Day32