当前位置: 首页 > news >正文

MyBatis学习笔记(上)

目录

MyBatis介绍

Mybatis简单示例

MyBatis编写流程

Mybatis参数

一.MyBatis参数详解

二.resultType

SqlMapConfig.xml配置文件

常见问题排查


MyBatis介绍

MyBatis 是一款轻量级 Java 持久层框架,核心价值是封装 JDBC 繁琐操作(如加载驱动、创建连接、处理结果集),让开发者聚焦 SQL 逻辑而非重复的 JDBC 代码。

核心设计思想

  1. 配置驱动:通过 XML 或注解定义 SQL 语句,避免硬编码。
  2. 参数映射:自动将 Java 对象与 SQL 动态参数绑定(如 #{username})。
  3. 结果映射:执行 SQL 后,自动将数据库结果集转换为 Java 对象(ORM 思想)。
  4. 低侵入性:不强制依赖复杂配置,可灵活集成到各类项目中。

Mybatis简单示例

1.创建库和表结构:

-- 1. 创建数据库
create database mybatis_db;
use mybatis_db;-- 2. 创建用户表
CREATE TABLE `user` (`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户ID(自增)',`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;-- 3. 插入测试数据
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','男','广州');

2.所需依赖:

<dependencies><!-- 1. MyBatis 核心依赖 --><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.4.5</version> <!-- 稳定版本,兼容性好 --></dependency><!-- 2. MySQL 驱动(适配 MySQL 5.x;若用 MySQL 8.x,需改为 8.0+ 版本,驱动类为 com.mysql.cj.jdbc.Driver) --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.6</version></dependency><!-- 3. 单元测试(JUnit 4,简化测试代码) --><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version><scope>test</scope> <!-- 仅测试环境生效 --></dependency><!-- 4. 日志(Log4j,打印 SQL 执行日志,便于调试) --><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency>
</dependencies>

补充:Log4j 配置(可选)

resources 目录下创建 log4j.properties,开启 SQL 日志打印:

# 日志输出级别:DEBUG(打印 SQL 细节)、INFO(仅关键信息)
log4j.rootLogger=DEBUG, Console# 控制台输出配置
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
# 日志格式:时间 | 线程 | 级别 | 类名 | 日志内容
log4j.appender.Console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%t] %-5p %c - %m%n# MyBatis 日志细化(可选)
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG

3.编写User实现类:

package com.mszlu.domain;import java.util.Date;/*** 用户实体类(对应数据库 user 表)*/
public class User {private Integer id;         // 对应表 id 列(自增)private String username;    // 对应表 username 列private Date birthday;      // 对应表 birthday 列(MyBatis 自动转换 datetime ↔ Date)private String sex;         // 对应表 sex 列private String address;     // 对应表 address 列// 无参构造器(MyBatis 反射创建对象时必须)public User() {}// 有参构造器(便于手动创建对象)public User(String username, Date birthday, String sex, String address) {this.username = username;this.birthday = birthday;this.sex = sex;this.address = address;}// Getter + Setter(MyBatis 需通过 Setter 注入结果集数据)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; }// toString(便于打印对象信息)@Overridepublic String toString() {return "User{" +"id=" + id +", username='" + username + '\'' +", birthday=" + birthday +", sex='" + sex + '\'' +", address='" + address + '\'' +'}';}
}

4.编写userMapper以及userMapper.xml

userMapper:定义接口

userMapper.xml(在resources目录下,创建mapper文件夹。编写UserMapper.xml的配置文件):定义sql语句

5.编写主配置文件SqlMapConfig.xml,SqlMapConfig.xml是MyBatis框架的核心配置文件。在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="123456"/></dataSource></environment></environments><!-- 加载映射的配置文件 --><mappers><mapper resource="mapper/UserMapper.xml"/></mappers>
</configuration>

6.编写测试方法

public class testDemo1 {@Testpublic void testFindAll() throws IOException {// 加载主配置文件,目的是构建SqlSessionFactory的对象InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");// 创建SqlSessionFactory对象SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);// 使用SqlSessionFactory工厂对象创建SqlSession对象SqlSession session = factory.openSession();// 通过session创建UserMapper接口的代理对象UserMapper mapper = session.getMapper(UserMapper.class);// 调用查询所有的方法//第一种方式:List<User> list = mapper.findAll();//第二种方式
//        List<User> userList = session.selectList("com.mszlu.mapper.UserMapper.findAll");// 遍历集合for (User user : list) {System.out.println(user);}// 释放资源session.close();in.close();}
}

MyBatis编写流程

MyBatis 推荐使用 “接口 + XML” 的方式定义 SQL:Mapper 接口定义方法签名,Mapper XML 定义具体 SQL 语句,两者通过全限定名 + 方法名关联(无需手动实现接口,MyBatis 动态生成代理对象)。

使用Dao进行增删改查

1.创建userMapper接口

package com.mszlu.mapper;import com.mszlu.domain.QueryVo;
import com.mszlu.domain.User;
import java.util.List;/*** User 表 Mapper 接口(定义 SQL 方法签名)*/
public interface UserMapper {// 1. 查询所有用户List<User> findAll();// 2. 测试列名与属性名不一致(需用 resultMap 映射)List<User> findAll2();// 3. 按 ID 查询用户(参数为简单类型:Integer)User findById(Integer userId);// 4. 新增用户(返回自增 ID,参数为 POJO:User)void insert(User user);// 5. 修改用户(参数为 POJO:User)void update(User user);// 6. 删除用户(参数为简单类型:Integer)void delete(Integer userId);// 7. 模糊查询用户(参数为简单类型:String)List<User> findByName(String username);// 8. 查询用户总数(返回简单类型:Integer)Integer findByCount();// 9. 按包装类查询(参数为包装类:QueryVo)List<User> findByVo(QueryVo queryVo);
}

2.在resources/mapper 目录下创建 UserMapper.xml,编写 SQL 语句与结果映射:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace:必须与 Mapper 接口的全限定名一致(关联接口与 XML)例:com.mszlu.mapper.UserMapper-->
<mapper namespace="com.mszlu.mapper.UserMapper"><!-- 1. 结果映射(resultMap):解决“表列名与实体类属性名不一致”问题 --><!-- id:resultMap 的唯一标识(供 select 标签的 resultMap 属性引用)type:映射的实体类类型(可直接用别名 user,对应 com.mszlu.domain.User)--><resultMap id="userMap" type="user"><!-- id 标签:映射主键列(性能优化,可选但推荐)result 标签:映射普通列property:实体类属性名column:表列名(或 SQL 查询的列别名)--><id property="id" column="id"/> <!-- 若列名与属性名一致,可省略该配置 --><result property="username" column="_username"/> <!-- 列别名为 _username,对应属性 username --><result property="birthday" column="birthday"/><result property="sex" column="sex"/><result property="address" column="address"/></resultMap><!-- 2. 查询所有用户(列名与属性名一致,用 resultType) --><!-- id:必须与 Mapper 接口的方法名一致(findAll)resultType:返回结果类型(实体类别名或全限定名,MyBatis 自动映射)--><select id="findAll" resultType="user">select * from user;</select><!-- 3. 查询所有用户(列名与属性名不一致,用 resultMap) --><!-- SQL 中 username 列取别名为 _username,需通过 userMap 映射到属性 username --><select id="findAll2" resultMap="userMap">select id, username _username, birthday, sex, address from user;</select><!-- 4. 按 ID 查询用户(简单参数,用 #{占位符}) --><!-- parameterType:参数类型(简单类型可省略,MyBatis 自动推断)#{id}:占位符,对应方法参数(简单类型时,占位符名称可任意,如 #{userId} 也可)--><select id="findById" parameterType="int" resultType="user">select * from user where id = #{id};</select><!-- 5. 新增用户(返回自增 ID) --><!-- <selectKey>:获取新增后的自增 ID(MySQL 用 last_insert_id() 函数)keyProperty:将自增 ID 注入到实体类的哪个属性(User.id)order:执行时机(AFTER:插入后执行,BEFORE:插入前执行)resultType:返回 ID 的类型--><insert id="insert" parameterType="user"><selectKey keyProperty="id" order="AFTER" resultType="int">select last_insert_id(); <!-- MySQL 系统函数:返回当前会话最后一次插入的自增 ID --></selectKey>insert into user (username, birthday, sex, address) values (#{username}, #{birthday}, #{sex}, #{address});</insert><!-- 6. 修改用户 --><update id="update" parameterType="user">update user set username=#{username}, birthday=#{birthday}, sex=#{sex}, address=#{address} where id=#{id};</update><!-- 7. 删除用户 --><delete id="delete" parameterType="int">delete from user where id = #{id};</delete><!-- 8. 模糊查询用户(两种方式:#{} 与 ${}) --><!-- 方式1:#{}(推荐,防 SQL 注入,需手动加 %) --><select id="findByName" parameterType="string" resultType="user">select * from user where username like #{username}; <!-- 调用时需传参数:"%王%"(如 mapper.findByName("%王%")) --></select><!-- 方式2:${}(不防 SQL 注入,直接拼接字符串,占位符只能是 ${value}) --><!-- <select id="findByName" parameterType="string" resultType="user">select * from user where username like '${value}';<!-- 调用时需传参数:"%王%" 或 "王%"(如 mapper.findByName("%王%")) --></select>--><!-- 9. 查询用户总数(返回简单类型) --><select id="findByCount" resultType="int">select count(*) from user; <!-- count(*) 返回 int 类型 --></select><!-- 10. 按包装类查询(嵌套参数,用 #{对象.属性}) --><!-- parameterType:包装类类型(QueryVo),通过 #{user.username} 引用嵌套对象的属性 --><select id="findByVo" parameterType="queryVo" resultType="user">select * from user where username = #{user.username};</select></mapper>

3.测试代码...

import com.mszlu.domain.QueryVo;
import com.mszlu.domain.User;
import com.mszlu.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.io.OutputStream;
import java.util.Date;
import java.util.List;public class UserTest {private InputStream in;private SqlSession session;private UserMapper mapper;@Beforepublic void init() throws IOException {in = Resources.getResourceAsStream("SqlMapConfig.xml");SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in) ;session = factory.openSession();mapper = session.getMapper(UserMapper.class);}@Afterpublic void destory() throws IOException {session.close();in.close();}@Testpublic void testFindAll() throws IOException {List<User> userList = mapper.findAll();for(User user : userList){System.out.println(user);}in.close();}@Testpublic void testFindAll2() throws IOException {List<User> userList = mapper.findAll2();for(User user : userList){System.out.println(user);}in.close();}@Testpublic void testFindById() throws Exception {User user = mapper.findById(4);System.out.println(user);in.close();}@Testpublic void testInsert() throws Exception {User user = new User();user.setUsername("美美2");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(5);session.commit();}// 第一种@Testpublic void testFindByName() throws Exception {List<User> list = mapper.findByName("%王%");for (User user : list) {System.out.println(user);}}// 第二种@Testpublic void testFindByName2() throws Exception {List<User> list = mapper.findByName("王");for (User user : list) {System.out.println(user);}}@Testpublic void testFindByCount() throws Exception {Integer count = mapper.findByCount();System.out.println("总记录数:"+count);}//包装类测试@Testpublic void testFindByVo() throws Exception {QueryVo queryVo = new QueryVo();User user = new User();user.setUsername("熊大");queryVo.setUser(user);List<User> userList = mapper.findByVo(queryVo);for (User user1 : userList) {System.out.println(user1);}}
}

模糊查询符号使用的区别

通过#{}可以实现preparedStatement向占位符中设置值,自动进行java类型和jdbc类型转换,#{}可以有效防止sql注入。 #{}可以接收简单类型值或pojo属性值。 如果parameterType传输单个简单类型值,#{}括号中可以是value或其它名称。

通过$可以将传入的内容拼接在中且不进行类型转换,${}可以将parameterType 传入的内容拼接在sql中且不进行jdbc类型转换, ${}可以接收简单类型值或pojo属性值,如果parameterType传输单个简单类型值,${}括号中只能是value。

特性

#{}

${}

本质

PreparedStatement 占位符(?

字符串直接拼接

类型转换

自动进行 Java 类型 ↔ JDBC 类型转换

不转换,直接拼接字符串

SQL 注入防护

支持(占位符不直接拼接 SQL)

不支持(直接拼接,有注入风险)

简单类型参数名称

可任意(如 #{id}

#{userId}

只能是 ${value}

适用场景

普通参数传递(推荐)

动态表名、动态排序字段(谨慎使用)


Mybatis参数

一.MyBatis参数详解

1、简单数据类型:java八大类+String(mybatis把String归为简单数据类型)

简单的写法:java.lang.Integer --> int integer Int Integer 都可以,框架提供简写的方式。

2、POJO(JavaBean实体类)对象类型,默认不能简写,可以配置

例子如:User对象

3、POJO包装对象类型,包含更多的实体类(即对象中引用其他对象)

如在QueryVo实体类中:包含了User对象

在mapper接口中,加入这个函数

public List<User> findByVo(QueryVo queryVo);

实现:

参数类型是QueryVo类,我们可以直接使用二级子类,比如user,name,role等,然后使用user里的属性再进行查询

<!--    根据封装类型来查询--><select id="findByVo" resultType="com.mszlu.domain.User" parameterType="com.mszlu.domain.QueryVo" >select * from user where username = #{user.username}</select>

测试类:

//包装类测试
@Test
public void testFindByVo() throws Exception {QueryVo queryVo = new QueryVo();User user = new User();user.setUsername("熊大");queryVo.setUser(user);List<User> userList = mapper.findByVo(queryVo);for (User user1 : userList) {System.out.println(user1);}
}

二.resultType

        1.返回简单数据类型

int double long(Java八大基本类型) String

        2.返回POJO数据类型

返回User对象类型

        3.resultMap结果类型

resultType 用法
直接指定一个实体类(比如 User),查询结果会自动装进这个类的对象里。
但要求:数据库查出来的列名(比如表的 username 列)和 实体类的属性名(比如 User 类的 username 属性)必须一模一样,才能对应上。

resultMap 用法
列名和属性名不一样时(比如表列是 user_name,类属性是 userName),用它来手动指定 “列名对应哪个属性名”,帮它们搭个桥。
另外,它还能处理复杂的情况:比如实体类里不仅有基本属性,还包含另一个实体类(一对一)或者一个列表(一对多),也能正确映射进去。

<!--配置resultMap,用来进行数据封装id="唯一的名称,用来被引用的"type="进行封装数据的类型"
-->
<resultMap id="userMap" type="com.qcbyjy.domain.User"><!--property="JavaBean中的属性"column="表中的字段"--><result property="id" column="_id"/><result property="username" column="_username" /><result property="birthday" column="_birthday" /><result property="sex" column="_sex" /><result property="address" column="_address" />
</resultMap>

对于sql语句:

select id, username _username, birthday, sex, address from user;

实际查询的字段是username,但返回的结果的字段是别名_username

所以当我数据库表结构如下:

而查询的sql语句以及resultMap映射是上面所写的时

sql语句返回的结果集中,所对应字段名字是id,_username,birthday,sex,address,在结果集中查找,只有_username可以对上,所以只有_username才有结果,其他都为空


SqlMapConfig.xml配置文件

1.1. 定义properties标签的方式管理数据库的信息

即把数据库信息定义properties标签中的方法,然后dataSource再使用

1.2在项目中定义jdbc.properties属性文件,存储数据库相关的信息,统一管理

1.2.1 jdbc.properties属性文件

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///mybatis_db
jdbc.username=root
jdbc.password=123456

1.2.2 编写sql配置文件

这样少了前面的<properties>标签中的配置

2、类型别名定义

  1. MyBatis自已有类型别名的注册类,编写int或者integer通过注册可以找到java.lang.Integer
  2. 我们自己也可以进行别名的注册
  1. SqlMapConfig.xml的配置文件
<!-- 定义别名 -->
<typeAliases><!--        把com.mszlu.domain.User使用user别名来显示,别名user User USER都可以,默认是忽略大写的 --><typeAlias type="com.mszlu.domain.User" alias="user"/><!-- 针对com.mszlu.domain包下的所有的类,都可以使用当前的类名做为别名 --><package name="com.mszlu.domain"/>
</typeAliases>

(注意xml中顺序, MyBatis 配置文件的标签顺序不符合规范。MyBatis 对 <configuration> 内部子标签的顺序有严格要求,必须按照官方指定的顺序排列,否则会报格式错误。)

官方规定的 <configuration> 子标签顺序为(按先后排列):

properties? → settings? → typeAliases? → typeHandlers? → objectFactory? → objectWrapperFactory? → reflectorFact

此时就可以成功修改了。

常见问题排查

  1. ClassNotFoundException(类找不到):检查 Mapper 接口全限定名、实体类路径是否与 XML 中 namespaceresultType 一致。
  2. BindingException(接口与 XML 绑定失败):检查 XML 中 id 是否与接口方法名一致,namespace 是否与接口全限定名一致。
  3. SQLSyntaxErrorException(SQL 语法错误):打印 SQL 日志(Log4j),检查生成的 SQL 是否正确(如参数拼接、表名 / 列名是否存在)。
  4. MySQL 8.x 驱动报错:需将驱动版本改为 8.0+,驱动类改为 com.mysql.cj.jdbc.Driver,URL 加时区参数 ?serverTimezone=UTC
http://www.lryc.cn/news/625058.html

相关文章:

  • 从双目视差图生成pcl点云
  • linux 内核 - 进程地址空间的数据结构
  • Chromium base 库中的 Observer 模式实现:ObserverList 与 ObserverListThreadSafe 深度解析
  • 套接字超时控制与服务器调度策略
  • 单例模式及优化
  • 高防IP如何实现秒级切换?
  • 【Day 30】Linux-Mysql数据库
  • IDE开发系列(2)扩展的IDE框架设计
  • STC8单片机矩阵按键控制的功能实现
  • 分治-归并-493.翻转对-力扣(LeetCode)
  • Flutter 自定义 Switch 切换组件完全指南
  • Python 面向对象三大特性详解(与 C++ 对比)
  • Android Handler 线程执行机制
  • flutter项目适配鸿蒙
  • 【展厅多媒体】互动地砖屏怎么提升展厅互动感的?
  • 2025年最新美区Apple ID共享账号免费分享(持续更新)
  • 数组学习2
  • Java面试题储备14: 使用aop实现全局日志打印
  • 【HTML】document api
  • Vue 3中watch的返回值:解锁监听的隐藏技巧
  • C++---有符号和无符号整数的位移操作
  • RabbitMQ:数据隔离
  • kafka 冲突解决 kafka安装
  • Unity进阶--C#补充知识点--【Unity跨平台的原理】Mono与IL2CPP
  • 探索性测试:灵活找Bug的“人肉探测仪”
  • MongoDB Windows 系统实战手册:从配置到数据处理入门
  • keil错误:Error: failed to execute ‘D:\Keil\C51\BIN\BIN\A51.EXE‘
  • 【智慧工地源码】智慧工地云平台系统,涵盖安全、质量、环境、人员和设备五大管理模块,实现实时监控、智能预警和数据分析。
  • PYTHON让繁琐的工作自动化-猜数字游戏
  • 从数据汇总到高级分析,SQL 查询进阶实战(下篇)—— 分组、子查询与窗口函数全攻略