MyBatis

tech2026-08-09  1

概述

持久层技术的底层解决方案: JDBC技术:Connection,PreParedStatement,ResultSet JDBC简单回顾:

Class.forName("com.mysql.jdbc.Driver") // 加载驱动 Connection conn = DriverManager.getConnection(URL, USER, PASSWORD); // 获取连接 Statement stmt = conn.createStatement(); // 获取statement PreparedStatement ptmt = conn.preparedStatement(sql); // 预编译sql语句 ResultSet rs1 = stmt.executeQuery("SELECT user_name, age FROM imooc_goddess"); // 执行sql语句 ResultSet rs2 = ptmt.execute(); rs.getString(itemName); // 获取结果 rs.getInt(itemName); rs.next();

mybatis是一个Java语言的持久层框架,封装了JDBC的细节,使开发者只需关注SQL语句本身,无需关心注册驱动,创建连接,创建statement等繁琐的过程。 并使用ORM思想实现了结果集的封装。 ORM: object relational mapping 对象关系映射 ORM就是把数据表和实体类以及实体类中的属性对应起来,让使用者可以通过操作实体类实现操作数据表。

入门案例

工程搭建:

创建maven工程并导入相关依赖创建实体类和持久层接口创建mybatis的主配置文件,在xml配置文件中完成环境配置。创建映射配置文件。 映射配置文件位置必须和dao接口的包名结构相同。 映射配置文件中mapper标签中的namespace属性,必须是dao接口的全限定类名。 映射文件中的操作配置,id属性的取值必须是dao接口中的方法名,resultType属性写名返回值的封装类型 <mapper namespace="com.meituan.domain.User"> <!-- 对应的实体类的全限定类名 --> <select id="findAll" resultType="com.meituan.domain.User"> <!-- 接口中的方法名,返回值封装类型 --> select * from user; </select> <insert id="saveUser" parameterType="com.meituan.domain.User"> insert into user(username, sex, age, address, birthday) value(#{username}, #{sex}, #{age}, #{address}, #{birthday}); <!-- 在出入语句执行之后获取插入id,相当于在执行插入操作之后再执行一条语句 --> <selectKey keyProperty="id" keyColumn="id" resultType="int" order="after"> select last_insert_id(); </selectKey> </insert> <update id="updateUser" parameterType="com.meituan.domain.User"> update user set (username=#{username}, sex=#{sex}, age=#{age}, address=#{address}, birthday=#{birthday}) where id=#{id}; </update> <delete id="deleteUser" parameterType="java.lang.Integer"> <!-- 此处的输入类型可以写java.lang.Integer或int --> delete from user where id=#{uid}; </delete> <!-- 在使用模糊查询时,在调用方法时添加%,这种方法更常用,因为利用到了预处理preparedStatement,效率更高 --> <!-- List<User> users = userDao.findByName("%王%"); --> <select id="findByName" parameterType="string" resultType="com.meituan.domain.User"> select * from user where username like #{name}; select * from user where username like '${name}'; <!-- 此时模糊查询时不需要% --> </select> <select id="count" resultType="int"> select count(id) from user; </select> </mapper>

mybatis使用步骤:

读取配置文件创建SqlSessionFactory工厂使用工厂生产SqlSession对象使用SqlSession对象创建Dao接口的代理对象使用代理对象执行方法释放资源 InputStream in = Resources.getResourceAsStream(xmlPath); // 读取配置文件 SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); // 构建者模式 SqlSessionFactory factory = builder.build(in); // 创建工厂,工厂模式 // autoCommit参数默认为false,我们可以将其设置为true,省略提交事务的步骤,但一般不采用,因为我们一般要将多个SQL语句组成一个事务,整体提交。 SqlSession session = factory.openSession(autoCommit=false); // 生产Session对象 IUserDao userDao = session.getMapper(IUserDao.class); // 创建Dao接口的代理对象 List<User> users = userDao.findAll(); // 使用代理接口执行方法 session.commit(); // 提交事务 session.close(); in.close(); // 释放资源

获取配置文件路径的安全方法:

使用类加载器,可以读取类路径的配置文件。使用ServletContext对象的getRealPath()方法获取当前程序运行的路径。 mybatis支持用户不用代理接口,自己写实现类,但会增加许多冗余代码,一般不用。

注解配置

首先需要在主配置文件中的mapper标签下,使用class属性指定添加注解的全限定类名。在使用xml配置文件时需要使用resource属性指定映射配置文件的包地址

<mappers> <mapper class="com.meituan.dao.IUserDao" /> </mappers>

SQL输入参数

输入参数: 在执行语句时,mybatis的输入可以是基本数据类型int,string或自定义数据类型。 当输入自定义数据类型时,在SQL语句中需要使用#{}或KaTeX parse error: Expected 'EOF', got '#' at position 36: …会自动利用OGNL进行解析。 #̲{}和{}的区别:#{}符号会在编译时将SQL语句中的对应字段变成?,通过prepareStatement中的set方法来赋值。能够有效避免SQL注入,更推荐使用。 ${}符号只是在编译阶段将相应字段替换成变量值。 OGNL表达式: (Object Graphic Navigation Language)对象图导航语言 这是一种通过对象的取值方式来获取数据的语言,在写法上忽略了get。 例如,通过类获取对象:user.getUsername(); 通过OGNL表达式获取:user.username

当我们需要通过两个以上的类进行查询时,可以将这些类重新封装成另一个查询类(vo),再将这一查询类作为输入参数输入到SQL语句中。解决实体类中的属性和表中的字段名称不同的问题: 在SQL语句上作修改,为查询后的字段取别名,此方法效率高,但需要需改每一条SQL语句。 添加配置,将查询后的字段和实体类中的属性一一对应,此方法效率低,但SQL语句无需修改,操作更方便。 <mapper namespace="com.meituan.domain.User"> <select id="findByVo" parameterType="com.meituan.domain.QueryVo" resultType="com.meituan.domain.User"> select * from user where username like #{user.username}; </selcet> <!-- 方法1. 当实体类中的属性名和表中的词条名不同时,可以通过给查询后的词条起别名的方法,将查询结果封装到实体类中 --> <select id="findAll" resultType="com.meituan.domain.User"> select id as userId, username as userName, sex as userSex, age as userAge, birthday as userBirthday from user; </select> <!-- 方法2. 将实体类中的属性和查询字段一一对应 --> <!-- 此时,方法的返回值需要配置成resultMap="userMap",此方法效率不高,通常不用 --> <resultMap id="userMap" type="com.meituan.domain.User"> <id property="userId" column="id" /id> <!-- 主键的配置,property为属性名,column为字段名 --> <result property="userName" column="username" /result> <result property="userSex" column="sex" /result> <!-- 非主键的配置,property为属性名,column为字段名 --> <result property="userAge" column="age" /result> <result property="userBirthday" column="birthday" /result> <resultMap> </mapper>

连接池

连接池一般用集合实现,连接池中存储着一些连接,他们在各个线程需要访问数据库时被各个线程调用,调用结束后放回连接池。类似于线程池。 连接池的特点是必须线程安全,且实现队列的特性。 可以在实际开发中节省许多创建连接的时间。 mybatis中的连接池: mybatis提供了三种配置连接池的方式: 在主配置文件中,利用dataSource标签中的type属性配置连接池。 mybatis中的三种连接池配置: POOLED:传统的javax.sql.DataSource规范中的连接池 UNPOOLED:不使用连接池,采用传统的获取连接方式 JNDI:采用服务器提供的JNDI技术来实现,获取DataSource 对象。如果不是web或者maven的war工程师不能使用的。 mybatis中连接池的调用原理: mybatis中维持了两个连接池,分别是空闲池和活动池。 相应伪代码:

if 空闲池中有连接: 将空闲池中的连接拿出 else if 活动池未满: 在活动池中创建一个新的连接 else: 将最旧的(oldest)连接返回

Mybatis中的事务

事务的ACID特性:

原子性(Atomicity):事务的内部操作不可分割,一个事务要么全部成功,要么全部失败,并且失败时不能对数据库产生任何影响。一致性(Consistency):事务的执行使数据库从一个状态转换成另一个状态,但不能影响数据库整体的稳定。隔离性(Isolation):当多个用户同时访问数据库时,各个事务之间不能相互影响。持久性(Durability):事务正确执行后,对数据库的影响是永久性的。

数据库的三范式: 第一范式:表中的字段具有原子性,即不可分割 第二范式:在第一范式的基础上,表中的所有非主键字段应该完全依赖于主键,即根据主键可以唯一的确定其他字段。 第三范式:在第二范式的基础上,表中的所有其他字段之间不存在依赖关系。

MyBatis中是使用sqlSession对象中的commit和rollback方法实现事务的提交和回滚。

动态SQL语句

MyBatis中可以动态的配置我们需要的SQL语句,具体可以通过以下标签实现: if标签 我们可以通过if标签动态配置SQL语句,具体用法为

<select id="findByCondition" resultType="com.meituan.domain.User" parameterType="com.meituan.domain.User"> select * from user where 1=1 <if test="username != null"> <!-- if中test属性后面的是Java语句,username对应的是类中的属性名。 --> and username = #{username} <!-- 可能接上的SQL语句,所以与要用and。前一个username是字段名称,后一个username是类中的属性名称 --> </if> <if test"sex != null"> and sex = #{sex} </if> </select>

select标签内为完整的SQL语句,当if条件成立时,将if标签内的语句与原语句拼接,否则忽视。 where标签 在使用if标签时,为了保证在if条件都不成立时SQL语句也能正常执行,我们通常要在SQL语句后面加上where 1=1,在MyBatis中我们可以利用where标签来去掉where 1=1操作

<select id="findByCondition" resultType="com.meituan.domain.User" parameterType="com.meituan.domain.User"> select * from user <where> <if test="username != null"> and username = #{username} </if> <if test="sex != null"> and sex = #{sex} </if> </where> </select>

foreach标签 在我们需要进行类似select * from user where id in(47, 48, 49);的查询时,foreach标签可以帮助我们动态配置SQL语句

<select id="findUsersById" parameterType="com.meituan.domain.IdList" resultType=> select * from user <where> <if test="ids != null and ids.size() > 0"> <!-- 此处的与也用and --> <foreach collection="ids" open="and id in (" close=")" item="id" seperate=","> #{id} </foreach> </if> </where> </select>

在实际项目中,我们还可以通过sql标签提取重复的sql语句

<sql id="defalutSelect"> select * from user </sql> <select id="findByCondition" resultType="com.meituan.domain.User" parameterType="com.meituan.domain.User"> <include refid="defaultSelect" /> <where> <if test="username != null"> <!-- if中test属性后面的是Java语句,username对应的是类中的属性名。 --> and username = #{username} <!-- 可能接上的SQL语句,所以与要用and。前一个username是字段名称,后一个username是类中的属性名称 --> </if> <if test"sex != null"> and sex = #{sex} </if> </where> </select>

注解配置

mybatis中的注解大致可以分为三类:

增删改查 @Insert @Update @Select @Delete结果集映射缓存
最新回复(0)