Mybatis使用方法的学习笔记总结

本文详细介绍MyBatis框架的使用方法,包括环境搭建、基本CRUD操作、动态SQL、多表查询、延迟加载及注解开发等内容,旨在帮助初学者快速上手并深入理解MyBatis。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Mybatis概述

Mybatis是一个持久层框架。它封装了jdbc的操作细节,使开发者只需要关注sql语句本身。它使用了ORM思想实现结果集的封装。

ORM(Object Relational Mapping):对象关系映射,简单来说就是将数据库表与实体类对应起来,数据库表的列名与实体类的属性对应起来。

Mybatis的入门使用

1、创建maven工程并导入依赖

这一步的详细步骤不再描述,是maven很基础的用法。

但是你很有可能遇到“Error : java 不支持发行版本5”这样的报错

原因在于IDEA默认创建的maven工程在"Settings" -->" Bulid, Execution,Deployment " --> "Java Compiler "中默认的jdk版本是5,需要改成你所用的jdk版本。

同时你还需要到"Project Structure" --> “Modules” --> "Depencies"中去把jdk版本改掉。

2、创建实体类和dao的接口

实体类就是User类

dao的接口就是SQL方法

IUserDao.java

/**
 * @Author Nino 2019/11/8
 *
 * 用户的持久层接口
 */
public interface IUserDao {

    /**
     * 查询所有操作
     * @return
     */
    List<User> findAll();

    /**
     * 保存用户
     * @param user
     */
    void saveUser(User user);

    /**
     * 更新用户
     *
     * @param user
     */
    void updateUser(User user);

    /**
     * 删除用户
     *
     * @param userId
     */
    void deleteUser(Integer userId);

    User findById(Integer userId);

    List<User> findByName(String userName);
}

3、创建Mybatis的主配置文件

SqlMapConfig.xml

这个文件头部是固定的,具体要做的是 配置Mysql的环境(注意,Mysql8以上的版本数据源连接池和以往配置有差别)和指定映射配置文件的位置(即每个dao配置的文件)

<?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">
<!-- mybatis的主配置文件 -->
<configuration>
    <!-- 配置环境 -->
    <environments default="mysql">
        <!-- 配置mysql的环境-->
        <environment id="mysql">
            <!-- 配置事务类型-->
            <transactionManager type="JDBC"></transactionManager>
            <!-- 配置数据源(连接池)-->
            <dataSource type="POOLED">
                <!-- 配置连接数据库的4个基本信息 -->
                <!-- 注意这里是mysql.cj.jdbc -->
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <!-- 注意时区的设置 -->
                <property name="url" value="jdbc:mysql://localhost:3306/ssm?serverTimezone=Asia/Shanghai&amp;characterEncoding=utf-8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>

    <!-- 指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件 -->
    <mappers>
        <mapper resource="com/nino/dao/IUserDao.xml"/>
<!--        <mapper class="com.nino.dao.IUserDao"/>  此方法是注解-->
    </mappers>
</configuration>

IUserDao.xml

映射配置文件

头文件是固定的。首先要做的是确认映射关系namespace,如果类的属性名与数据库列名不一致,则创建resultMap使他们一一对应。

接下来就是IUserDao.java的方法名与SQL语句的映射关系。有返回值的一定要写resultType或者resultMap(取决于类属性与列属性是否一致);方法有输入的,一定要写parameterType

<?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.nino.dao.IUserDao">

    <!-- 当类的属性和数据库属性不一致时 需要创建对应关系 -->
    <!-- 配置 查询结果的列名和实体类的属性名的对应关系 -->
    <resultMap id="userMap" type="com.nino.domain.User">
        <!-- 主键字段的对应 -->
        <id property="id" column="id"></id>
        <!-- 非主键字段的对应 -->
        <result property="address" column="address"></result>
        <result property="birthday" column="birthday"></result>
        <result property="sex" column="sex"></result>
        <result property="username" column="username"></result>
    </resultMap>

    <!-- 配置查询所有 -->
    <!--<select id="findAll" resultType="com.nino.domain.User">-->
    <select id="findAll" resultMap="userMap">
        select * from user ;
    </select>

    <insert id="saveUser" parameterType="com.nino.domain.User">
        insert into user(username,address,sex,birthday)values (#{username},#{address},#{sex},#{birthday});
    </insert>

    <update id="updateUser">
        update user set username=#{username},address=#{address},sex=#{sex},birthday=#{birthday} where id=#{id};
    </update>

    <delete id="deleteUser">
        delete from user where id=#{id};
    </delete>

    <select id="findById" parameterType="int" resultType="com.nino.domain.User">
        select * from user where id=#{id};
    </select>

    <select id="findByName" parameterType="string" resultType="com.nino.domain.User">
        select * from user where username like #{name};
    </select>
</mapper>

做完以上三步,即可对数据库进行CRUD操作了。

测试用例

在使用mybatis之前需要先做初始化(init()方法)

1、读取配置文件

2、使用SqlSessionFactoryBuilder创建对象并使用build()方法来创建SqlSessionFactory工厂

3、使用工厂创建SqlSession对象

4、使用SqlSession对象创建dao接口的代理对象

初始化后要做的就是使用代理对象的方法了

最后我们需要提交我们的请求以及释放资源(destory()方法)。

/**
 * mybatis的入门案例
 *
 * @Author Nino 2019/11/13
 */
public class MybatisTest {
    private InputStream in;
    private SqlSession sqlSession;
    private IUserDao userDao;

    @Before
    public void init() throws Exception {
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        sqlSession = factory.openSession();// 如果括号内输入true,则设置为自动提交,可以免去提交的代码
        userDao = sqlSession.getMapper(IUserDao.class);
    }

    @After
    public void destroy() throws Exception {
        // 不提交是不能成功插入数据的
        sqlSession.commit();
        // 释放资源
        sqlSession.close();
        in.close();
    }

    /**
     * 测试查找操作
     *
     * @throws Exception
     */
    @Test
    public void testFindAll() throws Exception {
        List<User> users = userDao.findAll();
        for (User user : users) {
            System.out.println(user);
        }
    }

    /**
     * 测试保存操作
     *
     * @throws ParseException
     */
    @Test
    public void testSave() throws ParseException {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-mm-dd");
        User user = new User("小明2号", dateFormat.parse("2000-01-13"), "男", "苏州市");

        userDao.saveUser(user);
    }

    @Test
    public void testUpdate() throws Exception{
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-mm-dd");
        User user = new User("小明", dateFormat.parse("2000-01-13"), "女", "扬州市");
        user.setId(6);
        userDao.updateUser(user);
    }

    @Test
    public void testDelete() {
        userDao.deleteUser(7);
    }

    @Test
    public void testFindOne() {
        User user = userDao.findById(6);
        System.out.println(user);
    }

    @Test
    public void testFindByName() {
        List<User> users = userDao.findByName("%i%");
        for (User user : users) {
            System.out.println(user);
        }
    }
}

标签学习

properties

SqlMapConfig.xml中,使用<properties>标签可以将jdbc的具体配置分离出来。

jdbcConfig.properties

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm?serverTimezone=Asia/Shanghai&characterEncoding=utf-8
jdbc.username=root
jdbc.password=123456

注意,properties中的&符号不再是xml文件中转义字符,因此没必要写成&amp;

相应的,SqlMapConfig.xml将如下,可以明显的看到,在加入properties配置后,配置数据库连接的信息从具体的值变成了${名称}

<?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">
<!-- mybatis的主配置文件 -->
<configuration>
    
    <!-- 配置properties -->
    <properties resource="jdbcConfig.properties"/>

    <!-- 配置环境 -->
    <environments default="mysql">
        <!-- 配置mysql的环境-->
        <environment id="mysql">
            <!-- 配置事务类型-->
            <transactionManager type="JDBC"></transactionManager>
            <!-- 配置数据源(连接池)-->
            <dataSource type="POOLED">
                <!-- 配置连接数据库的4个基本信息 -->
                <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>

    <!-- 指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件 -->
    <mappers>
        <mapper resource="com/nino/dao/IUserDao.xml"/>
<!--        <mapper class="com.nino.dao.IUserDao"/>  此方法是注解-->
    </mappers>
</configuration>
package

目的:提高开发效率

使用<typeAliases>将实体类打包,就可以对mapper的xml中的各种type直接赋值类名且无视大小写

SqlMapConfig.xml

...
<!-- 配置别名,只能配置domain里的类 -->
<!-- 注意要写在properties的后面,environment的前面 -->
    <typeAliases>
        <package name="com.nino.domain"/>
    </typeAliases>
...

IUserDao.xml

...
<!-- type都可以直接写user且不区分大小写 -->
<resultMap id="userMap" type="User"/>
<insert id="saveUser" parameterType="User"/>
<select id="findById" parameterType="int" resultType="User"/>
...

指定mapper接口时也可以使用package,与注释中语句的效果是一样的

<!-- 指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件 -->
    <mappers>
     <!--   <mapper resource="com/nino/dao/IUserDao.xml"/> -->
<!--        <mapper class="com.nino.dao.IUserDao"/>  此方法是注解-->
        <package name="com.nino.dao"/>
    </mappers>

Mybatis的连接池、事务及动态SQL

连接池

配置连接池可以减少我们获取连接所消耗的时间

 <!-- 配置数据源(连接池)-->
    <dataSource type="POOLED">
        <!-- 配置连接数据库的4个基本信息 -->
        <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>

上述代码中的type标签就是表示采取何种连接池方式

type属性有以下三个值

  • POOLED:采用传统的javax.sql.DataSource规范中的连接池
  • UNPOOLED:采用传统获取连接的方式,没有使用池的思想
  • JNDI:采用服务器提供的JNDI技术实现,来获取DataSource对象,不同的服务器拿到的DataSource是不一样的

事务(面试必考!)

什么是事务

事务的四大特性ACID

不考虑隔离性会产生的3个问题

解决办法:四种隔离级别

通过SqlSession对象的commit()rollback()方法实现事务的提交和回滚

动态SQL

含义

相比较***静态SQL***语句,***动态SQL***在编码时SQL语句还不能完整地写出来, 而是在程序执行时才能构造出来,这种在程序执行临时生成的SQL 语句叫***动态SQL***语句 。

where标签

最好用where标签来代替语句中写where,可以省去一些麻烦的事(比如必须要写1=1来避免and带来的困扰)

if 标签

接口中定义

// 通过传入的user对象的username对数据库进行查询
List<User> findByUserCondition(User user);

mapper中定义

<select id="findByUserCondition" resultMap="userMap" parameterType="user">
    select * from user
    <where>
        <if test="username != null">
            and username like #{username}
        </if>
    </where>
</select>

测试类中测试

@Test
public void testFindByUserCondition() {
    User user = new User();
    user.setUsername("%小明%");
    // 使用代理对象执行方法
    List<User> users = userDao.findByUserCondition(user);
    for (User u : users) {
        System.out.println(u);
    }
}

foreach标签

用于多个查询的SQL语句,如:

select * from user where id in(1,2,3)

针对这种语句,我们可以借助辅助类QueryVo.java,其中有个属性专门来存储id,用这种方式低耦合且方便增加或删除要查询的id

public class QueryVo {
    private List<Integer> ids;

    public List<Integer> getIds() {
        return ids;
    }

    public void setIds(List<Integer> ids) {
        this.ids = ids;
    }
}

接口中定义

// 根据QueryVo提供的id集合,查询用户信息
List<User> findByUserIds(QueryVo vo);

mapper中定义

<select id="findByUserIds" resultMap="userMap" parameterType="QueryVo">
    select * from user
    <where>
        <if test="ids != null and ids.size()>0">
            <foreach collection="ids" open="and id in (" close=")" item="id" separator=",">
                #{id}
            </foreach>
        </if>
    </where>;
</select>

foreach元素的属性主要有item,index,collection,open,separator,close。

collection表示传入的需要被遍历的结合

open表示该语句以什么开始

close表示以什么结束

item表示集合中每一个元素进行迭代时的别名(可以为任意值,同时要确保与#{}中的别名保持一致)

separator表示在每次进行迭代之间以什么符号作为分隔符

index指定一个名字,用于表示在迭代过程中,每次迭代到的位置

if标签中有一个test属性,test属性值是一个符合OGNL要求的判断表达式,表达式的结果可以使true或者false,除此之外所有的非0值都为true

抽取重复的SQL语句

(我觉得有点鸡肋)

定义

<!--抽取重复的sql语句-->
  <sql id="defaultUser">
    select * from user
  </sql>

语句

<include refid="defaultUser"></include>

实例应用

 <!--抽取重复的sql语句-->
  <sql id="defaultUser">
    <!-- 不要用分号,拼接的时候会出事 -->
    select * from user
  </sql>
 <!--配置查询所有 其中id不能乱写必须是dao接口中的方法 resultType写的是实体类的全路径-->
 <select id="findAll" resultMap="userMap">
   <include refid="defaultUser"/>
</select>

Mybatis的多表查询

SQL的JOIN

多表查询离不开SQL的JOIN语句(加不加OUTER据说都一样)

  • INNER JOIN:如果表中有至少一个匹配,则返回行
  • LEFT JOIN:返回左表所有的行,同时返回成功匹配到的右表的行
  • RIGHT JOIN:返回右表所有的行,同时返回成功匹配到的左表的行
  • FULL JOIN:只要其中有一个表中存在匹配,则返回所有的行

例句

select * from user u LEFT JOIN account a on u.id = a.UID;

一对一的关系

从表实体应该包含一个主表实体对象的引用。

例如用户和账户之间,用户可以拥有多个账户,但每个账户只属于一个用户。

账户类中需要包含一个用户对象来方便引用。

Account.java

...
private User user;
...

如此操作后,需要在mapper中定义一对一的关系映射,封装user的内容

<resultMap id="accountMap" type="Account">
    <id property="id" column="aid"/>
    <result property="money" column="money"/>
    <result property="uid" column="uid"/>
    <!-- 一对一的关系映射,配置封装user的内容,切记指定javaType-->
    <association property="user" column="uid" javaType="User">
        <id property="id" column="id"/>
        <result property="username" column="username"/>
        <result property="sex" column="sex"/>
        <result property="birthday" column="birthday"/>
        <result property="address" column="address"/>
    </association>
</resultMap>

注意!列名如果相同会出bug,因此有必要当建表时就取好不同的id名,或者在sql语句中写好别名

select u.*,a.ID as aid, a.UID,a.MONEY from user u LEFT JOIN account a on u.id = a.UID;

一对多的关系

一个用户拥有一堆账户,因此需要给用户类中添加一个存储账户的集合,这里我们可以取List<Account>

相应的mapper文件也要改动。

<resultMap id="userAccountMap" type="User">
    <id property="id" column="id"/>
    <result property="address" column="address"/>
    <result property="birthday" column="birthday"/>
    <result property="sex" column="sex"/>
    <result property="username" column="username"/>
    <!-- 一对多的关系映射,封装accounts -->
    <collection property="accounts" ofType="Account">
        <id property="id" column="aid"/>
        <result property="uid" column="uid"/>
        <result property="money" column="money"/>
    </collection>
</resultMap>

注意封装时一对多的集合(collection)和一对一的关系映射(association)不一样。

不一样的地方还有javaTypeofType

多对多的映射关系

一个人可以有多个身份(爸爸,老师),一个身份也可以由多个人组成,这就是多对多的关系。

具体代码不再赘述,掌握思想后可以参考一对多的代码来改。思想如下:

在实现多对多的关系时,双方的类都需要包含对方实体类的集合引用。

在建表时,如果只有user表和role表,是无法表示多对多的映射关系,需要新建一个user_role的辅助表来建立连接。

user_role中uid和rid都需要设置为主键和外键。

sql多表外链接查询语句参考

select u.*, r.id as rid,r.role_name,r.role_desc from role r   
left outer join user_role ur on r.id = ur.rid   
left outer join user u on u.id=ur.uid  

加油!虽然没有代码,但多对多的映射关系你已经懂了~

Mybatis的延迟加载

延迟加载即懒加载,如果被查询的对象有关联对象(比如:用户拥有的账户,账户所属的用户),除非需要,否则将不会主动查询关联对象的信息。

association,collection都可以实现延迟加载。

具体mapper中的代码要改写成这样

<resultMap id="userAccountMap" type="User">
    <id property="id" column="id"/>
    <result property="address" column="address"/>
    <result property="birthday" column="birthday"/>
    <result property="sex" column="sex"/>
    <result property="username" column="username"/>
    <collection property="accounts" column="id" ofType="Account" select="com.nino.dao.IUserDao.findById">
        <id property="id" column="aid"/>
        <result property="uid" column="uid"/>
        <result property="money" column="money"/>
    </collection>
</resultMap>

可以看到,collection中多了select和column属性。

select属性内容是查找关联对象的方法位置

column属性内容是给这个方法传入查询的参数

相应的sql语言将变为

<!-- select u.*,a.ID as aid, a.UID,a.MONEY from user u LEFT JOIN account a on u.id = a.UID; -->
<select id="findAll" resultMap="userAccountMap">
    select * from user;
</select>

<select id="findById" resultType="Account" parameterType="int">
    select * from account a where UID=#{id}
</select>

SqlMapConfig.xml中需要配置setting标签,位置在properties和typeAliases中间

<settings>
    <!-- 打开延迟加载的开关 -->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!-- 将积极加载改为消极加载,即延迟加载 -->
    <setting name="aggressiveLazyLoading" value="false"/>
</settings>

延迟加载完成,如果不主动调用用户的账户,在查询用户时是不会查询用户的账户信息的。

Mybatis的注解开发

CRUD

public interface IUserDao {

    /**
     * 查询所有用户
     * @return
     */
    @Select("select * from user")
    List<User> findAll();

    /**
     * 保存用户
     * @param user
     */
    @Insert("insert into user(username,address,sex,birthday)values(#{username},#{address},#{sex},#{birthday})")
    void saveUser(User user);

    /**
     * 更新用户
     * @param user
     */
    @Update("update user set username=#{username},sex=#{sex},birthday=#{birthday},address=#{address} where id=#{id}")
    void updateUser(User user);

    /**
     * 删除用户
     * @param userId
     */
    @Delete("delete from user where id=#{id} ")
    void deleteUser(Integer userId);
    
}

多表查询

一对多的关系用many=@many

一对一的关系用one=@one

案例可见:

一对一:

public interface IAccountDao {

    /**
     * 查询所有账户,并且获取每个账户所属的用户信息
     * @return
     */
    @Select("select * from account")
    @Results(id="accountMap",value = {
            @Result(id=true,column = "id",property = "id"),
            @Result(column = "uid",property = "uid"),
            @Result(column = "money",property = "money"),
            //这个注解是引入主表        FetchType(加载时机)  EAGER(立即加载)
            @Result(property = "user",column = "uid",one=@One(select="com.itheima.dao.IUserDao.findById",fetchType= FetchType.EAGER))
    })
    List<Account> findAll();

    /**
     * 根据用户id查询账户信息
     * @param userId
     * @return
     */
    @Select("select * from account where uid = #{userId}")
    List<Account> findAccountByUid(Integer userId);
}

一对多:

public interface IUserDao {

    /**
     * 查询所有用户
     * @return
     */
    @Select("select * from user")
    @Results(id="userMap",value={
            @Result(id=true,column = "id",property = "userId"),
            @Result(column = "username",property = "userName"),
            @Result(column = "address",property = "userAddress"),
            @Result(column = "sex",property = "userSex"),
            @Result(column = "birthday",property = "userBirthday"),
            @Result(property = "accounts",column = "id",
                    many = @Many(select = "com.itheima.dao.IAccountDao.findAccountByUid",
                                fetchType = FetchType.LAZY))
    })
    List<User> findAll();

    /**
     * 根据id查询用户
     * @param userId
     * @return
     */
    @Select("select * from user  where id=#{id} ")
    @ResultMap("userMap")
    User findById(Integer userId);

    /**
     * 根据用户名称模糊查询
     * @param username
     * @return
     */
    @Select("select * from user where username like #{username} ")
    @ResultMap("userMap")
    List<User> findUserByName(String username);

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值