MyBatis基础

MyBatis开发步骤

  1. 创建新模块
  2. 加入MyBatis和数据库驱动jar包
  3. 编写用户实体类(User)
  4. 准备核心配置文件:sqlMapConfig.xml
  5. 编写用户 dao 接口(UserMapper)
  6. 编写用户 dao 接口映射文件(UserMapper.xml)
  7. 编写测试代码

环境搭建

  1.   创建模块:mybatis_helloworld
    
  2.   加入MyBatis相关jar包:在模块下新建lib文件夹,复制mybatis框架jar包到lib文件夹下
      MyBatis框架包
      MySQL数据库驱动包
      log4j 日志包
    

MyBatis配置文件分类

MyBatis为了灵活,将数据库相关数据和SQL语句写到XML配置文件中。

  1. 核心配置文件:配置数据库的连接url,账号,密码,一般核心配置文件的名称:sqlMapConfig.xml
  2. 接口的映射文件:配置要执行的SQL语句,一般名称: XxxMapper.xml

核心配置文件配置了4个参数:

  1. driver:配置驱动的类名
  2. url:配置数据库的URL
  3. username:配置数据库的账号
  4. password:配置数据库的密码

核心配置文件sqlMapConfig.xml

namespace:  包名.接口名
id: 方法名
resultType: 方法的返回值类型
parameterType: 参数类型
#{xxx}: 首先使用?占位, 后面将xxx的值赋值给?
select标签主体内容: 查询的SQL语句
delete标签: 表示编写删除的SQL语句
insert标签: 表示添加数据的SQL语句
update: 表示执行修改的SQL语句

MyBatis入门案例:测试类

三大对象

  1. SqlSessionFactoryBuilder:负责构建SqlSessionFactory
  2. SqlSessionFactory:创建SqlSession实例的工厂
  3. SqlSession:用于执行SQL操作的对象

编写代码流程

  1. 创建SqlSessionFactoryBuilder对象
  2. 得到会话工厂SqlSessionFactory类
  3. 得到SqlSession对象
  4. 通过SqlSession对象得到Mapper接口的代理对象
  5. Mapper接口的代理对象执行数据库的查询操作
// MyBatis测试类
public class TestMyBatis {
    // 测试方法
    @Test
    public void testMyBatis() throws IOException {
        // 1.创建SqlSessionFactoryBuilder对象
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();

        // 2.得到会话工厂SqlSessionFactory类
        // 类路径:加载文件返回流, /表示到了src
//        InputStream in = TestMyBatis.class.getResourceAsStream("/sqlMapConfig.xml");
        // Resources: 这个类是MyBatis提供的,就是加载src下的资源,不要写/开头
        InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = builder.build(in);

        // 3.得到SqlSession对象, SqlSession相当于数据库的连接Connection
        SqlSession sqlSession = sqlSessionFactory.openSession();

        // 4.通过SqlSession对象得到Mapper接口的代理对象
        // getMapper返回接口的实现类.(使用动态代理生成接口的实现类)
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        // 5.Mapper接口的代理对象执行数据库的查询操作
        List<User> users = mapper.findAllUsers();

        User userById = mapper.findUserById(2);

        // 6.关闭资源
        sqlSession.close();

        for (User user : users) {
            System.out.println(user);
        }
    }
}

查询新增记录的主键值

子元素<selectKey>(常用)

<!--insert: 表示添加数据-->
<insert id="addUser" parameterType="com.itheima.entity.User">
    INSERT INTO user VALUES (null, #{username}, #{birthday}, #{sex}, #{address});
    <!--selectKey:获取插入数据的自增主键
        keyColumn: 数据库表中的主键字段
        keyProperty: 类中存放主键的成员变量
        resultType: 表中主键的类型
        order: 执行的时机:  BEFORE: 在执行SQL语句前查询主键 AFTER在执行SQL语句后查询主键
    -->
    <selectKey keyColumn="id" keyProperty="id" resultType="int" order="AFTER">
        SELECT LAST_INSERT_ID();
    </selectKey>
</insert>

在insert标签中增加属性(了解)

<!--insert:
useGeneratedKeys: 获取插入数据的自增主键
keyColumn: 数据库表中的主键字段
keyProperty: 类中存放主键的成员变量
-->
<insert id="addUser2" parameterType="com.itheima.entity.User" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
    INSERT INTO user VALUES (null, #{username}, #{birthday}, #{sex}, #{address});
</insert>

核心配置文件

properties标签

properties的作用

将外面的属性文件(.properties)加载进来。在后面就可以引用属性文件中的键和值

操作步骤

编写数据库连接属性资源文件(jdbc.properties)

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=root

在核心配置文件中通过properties标签加载jdbc.properties属性资源文件

<!--
属性:
resource: 指定类路径下Java的属性文件
-->
<properties resource="db.properties">
</properties>

<environments default="mysql">
    <environment id="mysql">
        <transactionManager type="JDBC"/>
        <dataSource type="POOLED">
            <!--1.3配置连接池需要的参数-->
            <property name="driver" value="${jdbc.driver}"/>
            <property name="url" value="${jdbc.url}"/>
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
        </dataSource>
    </environment>
</environments>

小结

  1. properties标签作用是什么?

    引入外部properties文件

  2. 如何使用外部properties文件中的数据

    ${键} : 取出这个键对应的值

typeAliases别名

typeAlias标签的作用:给类取别名

 <!--
    定义别名
    typeAlias 子元素
        type: 类全名
        alias: 别名,可以省略。默认使用类名做为别名,不区分大小写
    package子元素:
        给包里面所有类取别名, 别名就是类名,不区分大小写
 -->
 <typeAliases>
     <typeAlias type="com.kane.entity.User" alias="user"/>
     <package name="com.kane.entity"/>
</typeAliases>

package标签的作用

<typeAliases>
    <!--package标签:包扫描, 扫描这个包中的所有类,给这些类全部都取别名,别名就是类名小写
    name:包名-->
    <package name="com.kane.entity"/>
</typeAliases>

mappers(映射器)

加载单个映射文件

mapper标签的属性

<!--映射器-->
<mappers>
    <!--
    resource: 指定类路径下映射文件,注:路径使用/做为分隔符,而不是点号
    class: 指定使用注解的接口名字
     -->
    <mapper resource="mapper/UserMapper.xml"/>
</mappers>

注:如果是多级目录,是/而不是点号

包扫描加载多个映射文件

包扫描方式加载mapper映射文件

  1. 要求接口映射文件,与接口要放在同一个目录

  2. 要求接口映射文件的名称,与接口的名称要一致

<!--配置接口映射文件-->
<mappers>
    <!--
    package标签:
        接口映射文件与接口文件在同一个目录
        接口映射文件的名称与接口文件的名称相同
    -->
    <package name="com.kane.dao"/>
</mappers>

MyBatis模糊查询

在接口映射文件中编写相应的SQL语句

<select id="findUserByname" parameterType="string" resultType="user">
    SELECT * FROM user WHERE username LIKE #{username};
</select>

#{} 先使用?占位,后面给?赋值

​ 当参数是基本数据类型或者包装类或者String

​ #{随便写}

​ 当参数是自定义类型User

​ #{类中的成员变量名}

${} 字符串拼接,拿到参数和sql拼接(不建议使用)

​ 当参数是基本数据类型或者包装类或者String

​ ${value}

​ 当参数是自定义类型User

​ ${类中的成员变量名}

接口映射文件中方法的的参数类型可以省略

方法的返回值类型不能省略

映射文件的标签

配置mapper映射文件

  1. 定义resultMap标签
  2. id标签:映射主键字段,如果列名与属性名相同可以省略
  3. result标签:映射普通字段,指定哪个属性对应哪个列
  4. 在查询的结果中使用resultMap
<!--
   定义结果映射
       id: 映射的唯一标识
       type: 最终结果转换后的类型

   子元素:
       id: 定义主键字段的映射
       result: 定义普通的字段的映射
           属性:property: 实体类中属性名,column: 表中列名,如果相同可以不写。
->
<resultMap id="u1" type="user">
    <id column="id2" property="id"/>
    <result column="username2" property="username"/>
    <result column="birthday2" property="birthday"/>
</resultMap>
<select id="findUserByIdUseResultMap" parameterType="int" resultMap="u1">
    SELECT id id2, username username2, birthday birthday2, sex, address FROM user WHERE id = #{id};
</select>

动态SQL

动态SQL指的是:在程序运行时,根据不同的情况,拼接最终执行的sql语句。

搭建动态SQL的环境

模块名:mybatis_dynamic_sql

if标签

<if test="条件">
  SQL语句
</if>
if标签的作用
当if的条件为true就会拼接这个SQL片段

where标签

  1. 相当于where关键字,自动补全where这个关键字
  2. 去掉多余的and和or关键字

where标签的作用

1.充当where关键字
2.在需要的时候添加WHERE关键字
3.会去掉多余的AND或OR关键字

set标签

  1. 用在update语句中,相当于set关键字
  2. 去掉SQL代码片段中后面多余的逗号

set标签的作用

1.相当于SET关键字,在需要的时候添加SET关键字
2.去掉多余的,

foreach标签

foreach: 遍历数组或集合
  collection: 两个取值 遍历数组写array, 遍历集合写list
  open: 遍历前添加的内容
  close: 遍历后添加的内容
  item: 保存遍历得到的元素
  separator: 每次遍历后添加的内容

sql和include标签

  1. sql标签:定义一段SQL语句,起个名字可以重用。
  2. include标签:引入上面定义的SQL代码段。

动态SQL标签

if标签的格式:
    <if test="条件">
        SQL片段
    </if>

if标签的作用:当条件为true就会拼接SQL片段


<where></where>
where标签的作用:
	1.相当于WHERE关键字
	2.如果有条件就加上WHERE关键字,没有条件就会自动去掉WHERE关键字
	3.去掉多余的AND 或 OR
	
<set></set>
	1.相当于SET关键字
	2.去掉多余,
	
<foreach>
</foreach>

foreach标签的作用: 遍历数组或集合

collection属性: 要遍历的数组或集合,数组写array, 集合写list
open属性: 在遍历前拼接的内容
close属性: 在遍历后拼接的内容
item属性: 保存每次遍历的元素
separator: 分隔符,每次遍历完一个元素后添加的内容

多表关联

一对一关联

一对一关联查询实体类的关系

public class User {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;
    
    // 一个用户包含一个扩展信息
    private UserInfo userInfo;
}

在接口映射文件种使用哪个标签指定一对一关联

多表查询的时候使用resultMap封装结果时,即使字段名和类的属性名相同也需要指定。可以搞autoMapping=true

<resultMap id="userAndInfoMap" type="user" autoMapping="true">
    <!--
        association: 配置一对一关系: 一个用户对应一个扩展信息
            property: 类中另一方的成员变量名
            javaType: 类中另一方的类型
    -->
    <association property="userInfo" javaType="UserInfo" autoMapping="true">
    </association>
</resultMap>

<select id="findUserAndInfo" parameterType="int" resultMap="userAndInfoMap">
    SELECT * FROM USER INNER JOIN user_info ON user_info.id=user.id WHERE user.id=#{id};
</select>

一对多关联

public class User {

    private Integer id;   
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    private List<OrderForm> orders; // 对应的所有订单信息

    private UserInfo userInfo;  // 对应的用户信息

	// 省略getter/setter/toString
}
    <resultMap id="UserMoreOrderFormRM" type="user" autoMapping="true">
        <!--一对多一定要配置一方的主键,去掉重复的-->
        <id column="id" property="id"/>
        <!--collection:一对多配置
            property: 类中的成员变量名
            javaType: 外面的类型
            ofType: 里面的类型
            -->
        <collection property="orders" javaType="list" ofType="orderform" autoMapping="true">
        </collection>
    </resultMap>
    <!--(一对多)查询用户和对应的多个订单-->
    <select id="findUserAndOrders" resultMap="UserMoreOrderFormRM">
        SELECT * FROM USER INNER JOIN order_form ON user.id=order_form.user_id WHERE user.id=#{uid};
    </select>

多对多关联

多对多其实就是分成两个一对多处理

配置接口映射文件

查询一个用户对应多个角色

<resultMap id="userAndRoleMap" type="user" autoMapping="true">
    <id column="id" property="id"/>
     <!--一对多,多方的信息-->
     <collection property="roles" javaType="list" ofType="role" autoMapping="true">
         <id column="role_id" property="roleId"/>
     </collection>
 </resultMap>
 <!--通过uid查找用户和多个角色-->
 <select id="findUserAndRolesByUserId" resultMap="userAndRoleMap">
     SELECT * FROM USER INNER JOIN user_role ON user.id=user_role.user_id
     INNER JOIN role ON user_role.role_id=role.role_id WHERE user.id=#{uid};
 </select>

一个角色对应多个用户

<resultMap id="roleAndUserMap" type="role" autoMapping="true">
    <id column="role_id" property="roleId"/>
    <!--配置一对多的多方信息-->
    <collection property="users" javaType="list" ofType="user" autoMapping="true">
        <id column="id" property="id"/>
    </collection>
</resultMap>
<!--通过角色id rid查找角色和多个用户-->
<select id="findRoleAndUsersByRoleId" resultMap="roleAndUserMap">
    SELECT * FROM USER INNER JOIN user_role ON user.id=user_role.user_id
    INNER JOIN role ON user_role.role_id=role.role_id WHERE role.`role_id`=#{rid};
</select>

延迟加载

延迟加载概念:也叫懒加载。指的是按需加载,在实际用到数据的时候才加载。

如:查询用户信息,不需要他的扩展信息。但后面有可能又需要用到,这时候可以通过延迟加载来实现。当需要扩展信息的时候,再发送一条SQL语句来查询扩展信息。好处是,只有在需要的时候才查询相应数据。提升查询的效率。相当于每次只查询1张表,而不是一次使用表连接查询所有的信息。

<!--association:一对一配置
            select: 下一条SQL语句的方法名
            column: 这个字段的值会作为下一个SQL语句的参数
            fetchType: 加载类型
                lazy: 延迟加载(需要使用的时候才加载)
                eager: 立即加载(默认)-->

一对一关联查询使用标签:association

一对多关联查询使用标签:collection

association标签: 表示一对一的配置

association标签的属性说明
property另一方的成员变量名
column这个字段的值,作为下一个查询语句的参数
select下一条SQL语句
fetchTypelazy: 延迟加载

eager: 立即加载

collection标签的属性: 配置一对多

collection标签的属性说明
property多放的成员变量
column这个字段的值作为下一个条SQL语句的参数
select下一条要执行的SQL语句

MyBatis一级缓存

一级缓存是 sqlSession 范围的缓存,只能在同一个 sqlSession 内部有效。它本身已经存在,一级缓存不需要手动处理,可以直接使用

一级缓存测试

public class TestCache {
    // 测试一级缓存
    @Test
    public void testPrimaryCache() throws IOException {
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory factory = builder.build(inputStream);
        SqlSession session = factory.openSession();
        UserMapper userMapper = session.getMapper(UserMapper.class);  // 得到代理对象

        /*
         * 1.先根据id=1去一级缓存找
         * 2.没有找到,再查询数据库
         * 3.查询到结果后,放入一级缓存
         */
        User user1 = userMapper.findUserById(1);

        /*
         * 1.先根据id=1去一级缓存找
         * 2.找到,就直接返回,不查询数据库
         */
        User user2 = userMapper.findUserById(1);

        session.commit(); // 提交事务,清空一级缓存
        session.close();
    }
}

清空一级缓存

public class TestCache {
    // 测试一级缓存
    @Test
    public void testPrimaryCache() throws IOException {
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory factory = builder.build(inputStream);
        SqlSession session = factory.openSession();
        UserMapper userMapper = session.getMapper(UserMapper.class);  // 得到代理对象

        /*
         * 1.先根据id=1去一级缓存找
         * 2.没有找到,再查询数据库
         * 3.查询到结果后,放入一级缓存
         */
        User user1 = userMapper.findUserById(1);

        // 调用clearCache()方法,清空一级缓存的内容
        session.clearCache();
        // 当调用 SqlSession 的修改、添加、删除、提交、关闭等方法时,一级缓存会被清空。
        // userMapper.deleteUserById(5);

        /*
         * 1.先根据id=1去一级缓存找
         * 2.找到,就直接返回,不查询数据库
         */
        User user2 = userMapper.findUserById(1);

        session.commit(); // 提交事务,清空一级缓存
        session.close();
    }
}
  1. 缓存有什么好处?

    对于相同的查询语句,只会走一次数据库的查询,轻数据库的压力。

  2. 一级缓存的范围?

    同一个SqlSession有效

  3. 一级缓存何时失效?

    增删改,提交事务,关闭SqlSession主动清空缓存clearCache()
    

MyBatis二级缓存

二级缓存是 mapper 映射级别缓存,作用范围跨越SqlSession,即可以在多个 SqlSession 之间共享二级缓存
数据。

二级缓存关键点

  1. 实体类需要实现Serializable接口
  2. 至少要准备2个SqlSession,再进行测试

修改实体类实现Serializable接口

public class User implements Serializable {
    private Integer id; // 主键
    private String username; // 用户名
    private Date birthday; // 生日
    private String sex; // 性别
    private String address; // 地址
    // 省略其他
}

配置二级缓存

在 sqlMapConfig.xml 配置开启二级缓存,找到settings配置:

<settings>
    <setting name="cacheEnabled" value="true"/>
</settings>

在 UserMapper.xml 开启二级缓存使用

二级缓存还需要在具体的 mapper 映射文件中明确开启,这样做的原因是缓存数据要消耗资源,只有在需要使
用的时候开启,可以避免资源的过度消耗。

<mapper namespace="com.kane.dao.UserMapper">
    <!--开启二级缓存,当前Mapper里的所有查询的数据都会放入二级缓存中-->
    <cache/>
    
    <select id="findUserById" parameterType="int" resultMap="useMapOne">
        SELECT * FROM USER WHERE id=#{uid};
    </select>
</mapper>

测试

// 测试二级缓存
@Test
public void testSecondCache() throws IOException {
    SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
    InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
    SqlSessionFactory factory = builder.build(inputStream);


    SqlSession session1 = factory.openSession();
    UserMapper userMapper1 = session1.getMapper(UserMapper.class);
    User user1 = userMapper1.findUserById(1);
    session1.close(); // 需要先关掉第一个sqlsession

    SqlSession session2 = factory.openSession();
    UserMapper userMapper2 = session2.getMapper(UserMapper.class);
    User user2 = userMapper2.findUserById(1);

    session2.close();
}

二级缓存实现步骤

  1. 实体类实现Serializable

  2. 在核心配置文件中配置

    <settings>
        <!--开启二级缓存-->
        <setting name="cacheEnabled" value="true"/>
    </settings>
    
  3. 在接口映射文件中配置

    <mapper namespace="com.kane.dao.UserMapper">
        <!--让这个mapper开启二级缓存,这个mapper查询数据就会保存到二级缓存中-->
        <cache/>
    </mapper>
    
  4. 测试时需要使用两个不同的SqlSession.

注解开发方式

@Insert: 执行添加的SQL语句

@Update: 执行修改的SQL语句

@Delete: 执行删除的SQL语句

@SelectKey说明

属性说明
statement要执行的SQL语句:select last_insert_id()
keyProperty实体类中主键的属性
keyColumn表中主键的列名
resultType主键的数据类型
beforefalse 表示after,true表示before

注解说明

注解属性说明
@Results相当于resultMap表示要对结果进行映射
@Result对一个字段进行映射
column查询的字段名
property类中的成员变量名
idtrue 表示是主键

@Select注解作用:编写查询的SQL语句

@Results注解作用:相当于resultMap标签,对查询的结果进行手动封装(映射)

@Result注解作用:相当于result标签,将查询的字段值保存到类中的成员变量里面

MyBatis注解小结

在注解方式实现基本CRUD操作中,使用的注解有:

注解描述
@Select编写查询的SQL语句
@Results相当于resultMap,对查询的结果进行手动封装
@Result相当于result标签,对一个字段进行映射
@Update编写修改的SQL语句
@Delete编写删除的SQL语句
@Insert编写添加的SQL语句
@SelectKey获取添加数据后的自增的主键
@Param(“别名”)多个参数时需要取别名,不然会报错

复杂关系映射注解介绍

注解描述对应xml配置标签
@One用于一对一关联映射association
@Many用于一对多的关联映射collection

使用注解多表查询,不能使用内连接一次查出多张表的数据,只能懒加载的形式,分成多条SQL语句。

在UserMapper接口中增加查询方法

  1. 编写方法:通过id查询用户扩展信息

    1. 方法名:findUserInfoById
    2. 使用@Select注解编写SQL语句
  2. 编写方法:通过id查询1个用户

    1. 方法名:findUserById
    2. @Select编写查询
    3. @Results配置1对1关联映射

UserMapper接口

通过user_id查询当前用户订单的方法

  1. 编写findOrdersByUserId方法

  2. 使用@Select注解

  3. 修改findUserById()方法,增加1对多延迟加载配置

@Result注解属性说明
column这个字段的值会作为下一个SQL语句的参数
property类中的成员变量名
many表示一对多的配置
@Many注解属性说明
select下一条要执行的SQL语句的方法
fetchType获取数据的类型 FetchType.DEFAULT(立即加载), FetchType.LAZY(延迟加载)

@Insert(): 增加数据
@Update(): 修改数据
@Delete(): 删除数据
@Select(): 查询数据

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kane木易

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值