MyBatis框架与参数详解
MyBatis框架
- MyBatis框架概述
- MyBatis案例与详解
MyBatis框架概述
MyBatis 是一款基于 Java 语言的半自动化对象关系映射(ORM)持久层框架,其核心目标是对 JDBC API 进行抽象与封装,在保留开发者对 SQL 语句精细控制能力的前提下,它有效解决了传统 JDBC 开发中存在的流程冗余、业务逻辑与数据访问逻辑耦合、数据与对象映射复杂等问题。该框架最初由 Apache 软件基金会以“iBatis”为名开源,2010 年迁移至 Google Code 平台并正式更名为“MyBatis”,目前由 GitHub 社区主导维护,是 Java 生态中应用最广泛的持久层框架之一。
MyBatis 可通过 XML 或注解等主流方式,对需执行的 SQL 语句及对应的执行规则进行配置。框架会将 Java 对象中的数据与 SQL 语句中的动态参数进行绑定,进而执行 SQL 操作,SQL 执行完成后,MyBatis 会自动将数据库返回的结果集映射为 Java 对象并返回。
持久层是软件架构中负责数据持久化的核心层级,主要功能是将内存中的临时数据持久化到存储介质(如数据库),并能从存储介质中恢复数据至内存。作为连接业务逻辑层与数据存储层的桥梁,它可避免业务逻辑层直接与数据库交互导致的系统耦合度升高问题。
MyBatis案例与详解
创建数据库和表结构
create database mybatis_db;
use mybatis_db;
CREATE TABLE `user` (`id` int(11) NOT NULL auto_increment,`username` varchar(32) NOT NULL COMMENT '用户名称',`birthday` datetime default NULL COMMENT '生日',`sex` char(1) default NULL COMMENT '性别',`address` varchar(256) default NULL COMMENT '地址',PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into `user`(`id`,`username`,`birthday`,`sex`,`address`) values (1,'老、王','2018-02-27 17:47:08','男','北京'),(2,'熊大','2018-03-02 15:09:37','女','上海'),(3,'熊二','2018-03-04 11:34:34','女','深圳'),(4,'光头强','2018-03-04 12:04:06','男','广州');
创建 maven Java工程
引入坐标依赖,MyBatis的3.4.5的版本坐标,MySQL驱动的jar包5.1.6版本,Junit单元测试的jar包,引入log4j的jar包1.2.12版本(需要引入log4j.properties的配置文件)
<dependencies><!--mybatis核心包--><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.4.5</version></dependency><!--mysql驱动包--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.6</version></dependency><!-- 单元测试 --><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.10</version><scope>test</scope></dependency><!-- 日志 --><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency></dependencies>
log4j.properties配置文件
编写User的实现类,属性尽量使用包装类型
import java.io.Serializable;
import java.util.Date;public class User implements Serializable {private Integer id;private String username;private Date birthday;private String sex;private String address;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public Date getBirthday() {return birthday;}public void setBirthday(Date birthday) {this.birthday = birthday;}public String getSex() {return sex;}public void setSex(String sex) {this.sex = sex;}public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}@Overridepublic String toString() {return "User{" +"id=" + id +", username='" + username + '\'' +", birthday=" + birthday +", sex='" + sex + '\'' +", address='" + address + '\'' +'}';}
}
编写UserMapper的接口和方法
import java.util.List;public interface UserMapper {//查询所有的用户public List<User> findAll();}
在resources目录下,创建mapper文件夹,编写UserMapper.xml配置文件,导入约束文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qcby.mapper.UserMapper"><select id="findAll" resultType="com.qcby.domain.User">select * from user;</select></mapper>
通过namespace关联com.qcby.mapper.UserMapper接口,为接口中的findAll()方法绑定一条SQL语句,并指定查询结果会被映射为User对象。
编写主配置文件,在resources目录下创建SqlMapConfig.xml配置文件,导入对应的约束,编写主配置文件。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configurationPUBLIC "-//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"/><!-- 配置是否需要使用连接池,POOLED使用,UNPOOLED不使用 --><dataSource type="POOLED"><property name="driver" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql:///mybatis_db"/><property name="username" value="root"/><property name="password" value="root"/></dataSource></environment></environments><!-- 加载映射的配置文件 --><mappers><mapper resource="mappers/UserMapper.xml"/></mappers>
</configuration>
编写测试代码
import com.qcby.domain.User;
import com.qcby.mapper.UserMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;import java.io.InputStream;
import java.util.List;public class UserTest {/*** 测试查询所有的方法* @throws Exception*/@Testpublic void testFindAll() throws Exception {InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);SqlSession session = factory.openSession();UserMapper mapper = session.getMapper(UserMapper.class);List<User> list = mapper.findAll();for (User user : list) {System.out.println(user);}session.close();in.close();}@Testpublic void run() throws Exception {InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);SqlSession session = factory.openSession();List<User> list = session.selectList("com.qcby.mapper.UserMapper.findAll");for (User user : list) {System.out.println(user);}session.close();inputStream.close();}
}
实现效果:
执行testFindAll()
testFindAll () 函数通过 MyBatis 的接口代理方式实现数据库查询
首先利用 MyBatis 的 Resources 工具类加载主配置文件 SqlMapConfig.xml 的输入流,再通过 SqlSessionFactoryBuilder 构建出 SqlSessionFactory 会话工厂,随后打开 SqlSession 会话,调用 SqlSession 的 getMapper (UserMapper.class) 获取 UserMapper 接口的代理对象,调用代理对象的 findAll () 方法时,MyBatis 会自动匹配 UserMapper.xml 中对应 namespace 和 id 的 SQL 语句执行查询,得到 User 对象集合并打印,最后释放资源。这种方式面向接口编程,支持编译期类型检查,是 MyBatis 官方推荐的规范用法。
执行run()
run () 函数采用 MyBatis 传统的命名空间 + ID 方式执行查询
流程上同样先加载配置文件、创建 SqlSessionFactory 并打开 SqlSession,但执行查询时直接调用 SqlSession 的 selectList () 方法,通过字符串 “com.qcby.mapper.UserMapper.findAll” 定位目标 SQL,得到 User 对象集合并打印,最后释放资源。
实现增删改查
UserMapper接口代码
import com.qcby.domain.User;import java.util.List;public interface UserMapper {public List<User> findAll();public User findById(Integer userId);public void insert(User user);public void update(User user);public void delete(Integer userId);public List<User> findByName1(String username);public List<User> findByName2(String username);public Integer findByCount();}
UserMapper.xml的配置文件代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qcby.mapper.UserMapper"><select id="findAll" resultType="com.qcby.domain.User">select * from user;</select><!-- 通过id查询,SQL语句使用#{}占位符的名称,名称可以任意,仅限于基本数据类型和String类型--><select id="findById" resultType="com.qcby.domain.User" parameterType="int">select * from user where id = #{id};</select><!--保存操作--><insert id="insert" parameterType="com.qcby.domain.User"><selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">select last_insert_id();</selectKey>insert into user (username,birthday,sex,address) values (#{username},#{birthday},#{sex},#{address})</insert><!-- 修改 --><update id="update" parameterType="com.qcby.domain.User">update user set username = #{username},birthday = #{birthday},sex = #{sex},address=#{address} where id = #{id}</update><!-- 删除 --><delete id="delete" parameterType="Integer">delete from user where id = #{id}</delete><!-- 模糊查询1 --><select id="findByName1" resultType="com.qcby.domain.User" parameterType="string">select * from user where username like #{username}</select><!-- 模糊查询2,固定写法 --><select id="findByName2" resultType="com.qcby.domain.User" parameterType="string">select * from user where username like '%${value}%'</select><!-- 具体函数的查询 --><select id="findByCount" resultType="int">select count(*) from user</select>
</mapper>
UserTest 代码
package com.qcby;import com.qcby.domain.User;
import com.qcby.mapper.UserMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.List;public class UserTest1 {private InputStream in;private SqlSession session;private UserMapper mapper;@Beforepublic void init() throws Exception {in = Resources.getResourceAsStream("SqlMapConfig.xml");SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);session = factory.openSession();mapper = session.getMapper(UserMapper.class);}@Afterpublic void destory() throws IOException {in.close();session.close();}@Testpublic void testFindAll() throws Exception {List<User> list = mapper.findAll();// 遍历for (User user : list) {System.out.println(user);}in.close();}@Testpublic void testFindById() throws Exception {User user = mapper.findById(41);System.out.println(user);in.close();}@Testpublic void testInsert() throws Exception {User user = new User();user.setUsername("美美");user.setBirthday(new Date());user.setSex("男");user.setAddress("顺义");mapper.insert(user);session.commit();System.out.println(user.getId());}@Testpublic void testUpdate() throws Exception {User user = mapper.findById(4);user.setUsername("小凤");mapper.update(user);session.commit();}@Testpublic void testDelete() throws Exception {mapper.delete(2);session.commit();}// 第一种@Testpublic void testFindByName() throws Exception {List<User> list = mapper.findByName1("%王%");for (User user : list) {System.out.println(user);}}// 第二种@Testpublic void testFindByName1() throws Exception {List<User> list = mapper.findByName2("王");for (User user : list) {System.out.println(user);}}@Testpublic void testFindByCount() throws Exception {Integer count = mapper.findByCount();System.out.println("总记录数:"+count);}
}
实现效果:
执行 testInsert() 方法
该方法执行的是这条SQL语句
<insert id="insert" parameterType="com.qcby.domain.User"><selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">select last_insert_id();</selectKey>insert into user (username,birthday,sex,address) values (#{username},#{birthday},#{sex},#{address})</insert>
其中selectKey标签用于获取插入记录后自动生成的主键值,keyProperty=“id” 表示将查询到的主键值赋值给传入User对象的id属性,order=“AFTER” 指定该查询在insert语句执行之后执行(自增长主键是在记录插入后生成的),resultType=“java.lang.Integer” 声明查询返回的主键类型为Integer,内部 SQL select last_insert_id();
是 MySQL 的内置函数,用于获取当前会话中最后一次插入操作生成的自增长主键值。MyBatis 通过 <selectKey>
获取到主键值后,会主动调用对象的setId()方法赋值,而由于对象是引用传递,外部的user对象就能通过getId()获取到这个值。
session.commit() 的核心功能是提交事务,确保数据库修改持久化。在 MyBatis 中,默认采用 JDBC 事务管理机制(对应配置中的 <transactionManager type="JDBC"/>
),通过 factory.openSession() 获取的 SqlSession 默认开启事务且不自动提交。当执行 mapper.insert(user) 时,插入操作仅暂存于事务中,直到调用 session.commit() 才会将数据永久写入数据库,完成数据持久化,若未显式调用 commit(),SqlSession 关闭时会自动执行事务回滚,所有未提交的修改都将被撤销,数据库不会保留新增数据。
模糊查询符号使用的区别
#{}
本质是MyBatis对JDBC中PreparedStatement占位符机制的封装,会被解析为SQL中的?
,后续通过PreparedStatement的setXxx()方法将参数值安全传入。它具备自动类型转换能力,可根据Java参数类型(如String、Integer)与数据库字段的JDBC类型(如VARCHAR、INT)完成适配,无需手动处理兼容问题。同时,因参数以纯数据形式通过预编译占位符传入,特殊字符会被转义,能有效抵御SQL注入攻击。在参数接收上,#{}可处理简单类型值(如int、String)与POJO对象的属性值,当parameterType为单个简单类型时,其括号内的名称可任意填写,MyBatis均能正确识别。${}
采用直接字符串替换的处理逻辑,不会生成SQL占位符,而是将参数值直接拼接至SQL语句中,且不进行任何JDBC类型转换,无论Java参数是int型还是String型,最终都会以字符串形式拼入。它同样支持接收简单类型值与POJO属性值,但在参数规则上与#{}存在差异:当parameterType为单个简单类型时,${}括号内必须填写“value”,否则MyBatis无法识别对应参数,这是其与#{}在单个简单类型参数处理上的核心区别。
实现效果:
MyBatis参数详解
- parameterType
parameterType 是 MyBatis 中用于指定 SQL 语句接收参数类型的配置属性。它主要作用是告诉 MyBatis 传入 SQL 的参数数据类型,以便框架正确解析和处理参数。该属性的值可以是基本数据类型(如 int、String)、包装类(如 Integer、Long)、自定义实体类、Map 等集合类型。在 Mapper 接口方法中,当参数为单个简单类型时,parameterType的取值可以是Java 类型的全限定名(如java.lang.Integer),也可以是 MyBatis 内置的类型别名(如int);但对于复杂类型(如自定义对象),parameterType需指定该类的全限定名或自定义别名。 - resultType
resultType 用于指定 SQL 查询语句返回结果的类型,它告诉 MyBatis查询结果需要被映射成什么类型的对象,MyBatis 会根据该类型,将 SQL 执行后的结果集自动封装为对应的对象或数据结构,它适用于结果集字段与 Java 对象属性名完全一致的场景。MyBatis 会根据 resultType 定义的类型,自动将查询结果集中的列值映射到对应类型的对象属性中。其取值可以是基本数据类型(如 String、int)、自定义实体类、List 等集合类型(此时需指定集合中元素的类型)。 - resultMap
resultMap 是 MyBatis 中用于自定义结果映射规则的配置标签,适用于结果集字段与 Java 对象属性名不一致,或存在复杂关联关系(如一对一、一对多)的场景。它通过(主键映射)和(普通字段映射)标签,手动指定数据库列名与 Java 对象属性名的对应关系,解决字段名与属性名不匹配的问题。此外,resultMap 还支持通过(一对一关联)和(一对多关联)标签处理关联查询,实现复杂对象的嵌套映射。与 resultType 相比,resultMap 的优势在于灵活性和可复用性,一旦定义可在多个 SQL 语句中引用,尤其适合处理复杂的数据映射场景。
类型别名定义
MyBatis 内置了一些常用类型的别名(如int对应Integer、string对应String、list对应ArrayList等),开发者无需手动定义即可直接使用。
也可以进行别名的注册,在SqlMapConfig.xml配置文件中设置
<!-- 定义别名 -->
<typeAliases><!-- 把com.qcby.domain.User使用user别名来显示<typeAlias type="com.qcby.domain.User" alias="user"/>--><!-- 针对com.qcby.domain包下的所有的类,都可以使用当前的类名做为别名 --><package name="com.qcby.domain"/>
</typeAliases>
在 MyBatis 的核心配置文件中,使用<typeAlias>
标签单独指定某个类的别名,或通过<package>
标签批量扫描某个包下的所有类,此时 MyBatis 会默认以类名(首字母大小写均可,不区分大小写)作为别名,例如扫描com.qcby.domain包后,User类可直接用user或User作为别名
<select id="findAll" resultType="user">select * from user
</select>
定义后的类型别名可直接在 Mapper 配置文件的parameterType、resultType等属性中使用,其值不再是原来的全路径了