前言:这是笔者学习黑马程序员JDBC和MyBatis课程的学习笔记。在阅读这篇文章学习之前,你需要了解maven的用法、java语言基础、sql数据库基础。
初学者可以直接开始学习MyBatis的内容,而跳过JDBC部分和连接池部分的学习。
文章主要是视频课程知识点记录。许多知识点是通过例子的形式说明的。
目录
1. JDBC
mybatis是一款持久层框架,用于简化jdbc的开发。而jdbc是java官方为了统一数据库使用而制定的一套数据库交互接口。
Java EE的三层架构:表现层、业务层、持久层(将数据保存到数据库的那一层)
1.1 jdbc入门例子(mysql+maven项目)
maven的快速上手参考我的另一篇博客【Maven】快速上手maven基础-CSDN博客前3部分内容。
引入依赖
首先创建一个maven项目。在pom.xml文件中,dependencies块中加入以下内容。
<dependencies> <!-- 加入以下内容 --> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <version>8.2.0</version> </dependency> </dependencies>
创建并使用连接
package org.example; //引入官方定义的jdbc import java.sql.*; public class App { public static void main( String[] args ) throws SQLException, ClassNotFoundException { //1、注册驱动(告诉jdbc使用的是什么数据库) Class.forName("com.mysql.cj.jdbc.Driver"); //2、获取连接 String url = "jdbc:mysql://localhost:3306/db_22331030"; String userName = "root"; String password = "root"; Connection conn = DriverManager.getConnection(url,userName,password); //定义sql语句 String sql = "UPDATE customer_info SET CustomerName = 'what' WHERE (`CustomerID` = '4');\n "; //获取执行sql的语句对象 Statement stmt = conn.createStatement(); //执行sql stmt.executeUpdate(sql); } }
1.2 jdbc API
1.2.1 DriverManager
驱动管理类,作用:注册驱动、数据库连接
该类是一个静态工具类,常用方法有:
getConnection(String url, String user, String password)
:依据指定的 URL、用户名和密码来获取数据库连接。getConnection(String url, Properties info)
:通过包含连接参数的Properties
对象来获取数据库连接。registerDriver(Driver driver)
:手动注册一个 JDBC 驱动程序(在 JDBC 4.0 之后通常不需要这样做)。
ps:
在上面例子中,注册驱动的方式是使用语句Class.forName()。
Class.forName()是Java 反射机制中的一个重要方法,主要用于在运行时动态加载类。而由Driver类的定义中可以看出,当Driver被加载的时候,执行的静态代码块会进行驱动的注册。
【Driver类的定义】
1.2.2 Connection
数据库连接对象。主要功能:获取执行sql语句的对象,管理事物。
- 获取sql执行对象 常用方法
- Statement createStatement():普通执行sql对象
- PreparedStatement prepareStatement():预编译 SQL 的执行 SQL 对象:防止 SQL 注入
- CallableStatement prepareCall():执行存储过程的对象
管理事物
java中可以利用try-catch语句进行事务操作:
【例子】
import java.sql.*; public class TransactionDemo { public static void main(String[] args) { String url = "jdbc:mysql://localhost:3306/testdb"; String user = "root"; String password = "password"; Connection conn = DriverManager.getConnection(url, user, password); Statement stmt = conn.createStatement(); try{ // 1. 开启事务(关闭自动提交) conn.setAutoCommit(false); stmt.executeUpdate("INSERT INTO accounts (name, balance) VALUES ('Alice', 1000)"); stmt.executeUpdate("INSERT INTO accounts (name, balance) VALUES ('Bob', 2000)"); // 3. 提交事务 conn.commit(); System.out.println("事务提交成功"); } catch(Exception e){ conn.rollback(); System.out.println("事务回滚: " + e.getMessage()); } stmt.close(); conn.close(); } }
1.2.3 Statement
作用:执行sql语句
执行 SQL 语句的方法
- executeUpdate():
- 用于DML语句(增删改)和DDL语句(数据定义语言);
- 执行DML语句,返回受影响的函数
- 执行DDL语句,执行成功返回0;失败抛出异常
- executeQuery():
- 用于DQL语句(数据查询语言);
- 返回ResultSet(结果集对象),通过该对象可以遍历和获取查询到的数据。
1.2.4 ResultSet
结果集对象,executeQuery()方法执行之后的返回值。
核心功能
数据遍历 boolean next():将光标从当前位置向后移动一行(结果集游标默认从第一行的上一行开始),如果移动后的行是有效行,返回true;否则返回false
获取数据 getXxx(参数):对于当前行,获取Xxx类型的数据。如getString()、getInt()、getDate()……参数有两种重载形式,int类型参数代表列的编号,String类型参数代表列的名称。
一般使用形式
while(rs.next()){
//获取数据
rs.getXxx();
}
【完整例子】
public class App1 { public static void main( String[] args ) throws SQLException, ClassNotFoundException { //1、注册驱动(告诉jdbc使用的是什么数据库) Class.forName("com.mysql.cj.jdbc.Driver"); //2、获取连接 String url = "jdbc:mysql://localhost:3306/db_22331030"; String userName = "root"; String password = "root"; Connection conn = DriverManager.getConnection(url,userName,password); //定义sql String sql = "select * from customer_info ;"; //获取执行sql的语句对象 Statement stmt = conn.createStatement(); //执行sql ResultSet rs=stmt.executeQuery(sql); //遍历结果集 while (rs.next()){ int id = rs.getInt("CustomerID");//获取列名为CustomerID的数据 String name = rs.getString("Customername"); String company = rs.getString(3); //第3列数据(索引从1开始) String sex = rs.getString(4); System.out.println(id+"--"+name+"--"+company+"--"+sex); } rs.close(); stmt.close(); conn.close(); } }
1.2.5 PreparedStatement
PreparedStatement继承自Statement。其作用是预编译SQL语句并执行,预防SQL注入问题。
【SQL注入】
通过一些特殊输入来修改实现定义好的sql语句,用以达到执行代码对服务器进行攻击的方法。
【例子】
正常 SQL 查询:
SELECT * FROM users WHERE username = '输入的用户名' AND password = '输入的密码';
恶意输入示例:
' OR '1'='1
拼接后被篡改的 SQL:
SELECT * FROM users WHERE username = '' OR '1'='1' AND password = '';
由于
'1'='1'
恒为真,攻击者可以绕过身份验证直接访问数据库。
mysql默认关闭预编译功能,如果需要使用注意要开启
使用过程
public class App2 { public static void main( String[] args ) throws SQLException, ClassNotFoundException { Class.forName("com.mysql.cj.jdbc.Driver"); //使用参数useServerPrepStmts开启预编译功能 String url = "jdbc:mysql://localhost:3306/db_22331030?useServerPrepStmts=true"; String userName = "root"; String password = "root"; Connection conn = DriverManager.getConnection(url,userName,password); //定义sql,参数部分使用?占位符 String sql = "select * from customer_info where Customername = ? and password = ?"; //获取执行sql的语句对象,参数是sql语句 PreparedStatement pstmt = conn.prepareStatement(sql); //设置参数 pstmt.setString(1,"张三"); //设置Customername pstmt.setString(2,"123456");//设置password //执行语句 ResultSet rs=pstmt.executeQuery(); //判断结果 if (rs.next()){ System.out.println("登录成功"); }else{ System.out.println("登录失败"); } //释放资源 pstmt.close(); rs.close(); conn.close(); } }
其原理是将参数传入的时候,会进行转义,如将 ' 转义成 \' 。即,输入的符号 ' 不会与预定义好的sql语句中的 ' 相匹配。
原理
传统 Statement 的问题 普通Statement
通过字符串拼接 SQL,每次执行都需要数据库重新解析和编译 SQL
预编译流程
- 发送 SQL 模板:客户端将带占位符
?
的 SQL(如SELECT * FROM users WHERE username = ?
)发送到数据库。 - 数据库编译:数据库解析 SQL 结构,生成执行计划并缓存。
- 多次执行:后续只需传递参数值,无需重复编译,提升效率。
2.数据库连接池
2.1 基本概念
- 是一个容器,负责分配、管理数据库连接。
- 允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。
- 释放空闲时间超过最大空闲时间的数据库连接,避免因为没有释放数据库连接而引起的数据库连接遗漏(连接池没有空闲连接了,导致某个用户一直等不到连接)。
2.2 数据库连接池实现
标准接口:DataSource
- 由官方SUN公司提供的数据库连接池的标准接口,其他任何第三方连接池都需要实现该接口。
- 功能:获取连接
常见数据库连接池
- DBCP
- C3P0
- Druid:阿里巴巴开源的项目;是一个功能强大、性能优秀、Java语言最好的连接池之一。
2.3 Driud的使用
下面通过一个例子说明Driud的使用。
首先创建一个maven项目。
引入依赖
pom.xml
<!--引入druid连接池--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.16</version> </dependency> <!--引入mysql连接驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency>
创建druid的properties配置文件
创建一个后缀名为.properties的文件,参考一下格式进行配置。
其中必要的参数有:
- driverClassName:数据库驱动类名。这里使用的是mysql因此使用mysql的驱动
- url:数据库连接地址。格式:
jdbc:mysql://主机名:端口号/数据库名?参数
- username:数据库登录用户名
- password:数据库登录密码
#druis.properties #基本参数: driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://127.0.0.1:3306 username=root password=root #可选参数(参数有很多,具体查看官方文档) #初始化连接数量 initialSize=5 #最大连接数 maxActive=10 #最大等待时间 maxWait=34000
获取连接对象
public class DruidUsing { public static void main(String[] args) throws Exception { //加载配置文件 Properties prop=new Properties(); prop.load(new FileInputStream("配置文件路径\\druid.properties")); //获取连接池对象 DataSource dataSource= DruidDataSourceFactory.createDataSource(prop); //获取数据库连接 Connection Connection conn=dataSource.getConnection(); System.out.println(conn); } }
3. MyBatis
JDBC的缺点:
- 硬编码
- 注册驱动、获取连接
- SQL语句
- 操作繁琐
- 手动设置参数
- 手动封装结果集
mybatis免除了几乎所有jdbc代码以及设置参数和获取结果集的工作
3.1 mybatis快速入门
官方教程MyBatis 3 插件 |入门 – mybatis
创建user表,添加数据
create database mybatis; use mybatis; drop table if exists tb_user; create table tb_user( id int primary key auto_increment, username varchar(20), password varchar(20), gender char(1), addr varchar(30) ); INSERT INTO tb_user VALUES (1, 'zhangsan', '123', '男', '北京'); INSERT INTO tb_user VALUES (2, '李四', '234', '女', '天津'); INSERT INTO tb_user VALUES (3, '王五', '11', '男', '西安');
创建模块,导入坐标
创建maven模块,将mybatis等必要依赖的坐标导入。
<dependencies> <!-- 添加mybatis依赖--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.5</version> </dependency> <!-- 导入Junit单元测试依赖--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> </dependencies>
编写MyBatis核心配置文件
在resources文件夹下创建配置文件mybatis-config.xml
注意将数据库连接信息改成自己的。
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "https://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <!--数据库连接信息--> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis?useSSL=false"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> <mappers> <!--指定映射文件路径--> <mapper resource="UserMapper.xml"/> </mappers> </configuration>
编写SQL映射文件
需要管理Xxx表,就要创建一个与之对应的Xxx实体类。因为管理的是user表,创建一个User实体类,类的成员变量就是表中的各个字段。
public class User { private Integer id ; private String username ; private String password ; private String gender ; private String addr ; //下面还包含所有属性的getter和setter方法,以及toString(). }
创建一个与要管理的Xxx表对应的XxxMapper.xml的SQL映射文件。这里我们要操作前面创建的user表,因此我们在resources文件夹下创建UserMapper.xml文件。
需要在该配置文件的<mapper>块里面配置sql语句的映射,其中这里面指定了一个resultType(返回值类型),因为下面例子中用到的是查询语句(select标签)所以返回值类型应该是封装好的一个个的实体类,也就是刚刚创建好的类。
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!--namespace:名称空间,理论上可以自己命名--> <mapper namespace="test"> <!--id:sql语句的唯一标识 resultType:返回值类型--> <select id="selectAll" resultType="org.example.pojo.User"> select * from tb_user; </select> </mapper>
编码
public class App { public static void main( String[] args ) throws Exception { //加载mybatis的核心配置文件,获取SqlSessionFactory对象 String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //获取sqlSession对象,用它来执行sql SqlSession sqlSession = sqlSessionFactory.openSession(); //执行sql List<User> users=sqlSession.selectList("test.selectAll"); System.out.println(users); //释放资源 sqlSession.close(); } }
运行上面代码,输入如下:
3.2* Mapper代理开发
3.1小结的开发方式存在依赖于字符串字面量的硬编码问题。使用mapper代理方式能解决硬编码问题和简化后期SQL执行。
3.2.1 使用Mapper代理的原则
- 定义与 SQL 映射文件同名的 Mapper 接口,并且将 Mapper 接口和 SQL 映射文件放置在同一目录下(编译后)
- 设置 SQL 映射文件的 namespace 属性为 Mapper 接口全限定名
- 在 Mapper 接口中定义方法,方法名就是 SQL 映射文件中 sql 语句的 id,并保持参数类型和返回值类型一致
- 编码
- 通过 SqlSession 的 getMapper 方法获取 Mapper 接口的代理对象
- 调用对应方法完成 sql 的执行
细节:如果 Mapper 接口名称和 SQL 映射文件名称相同,并在同一目录下,则可以使用包扫描的方式简化 SQL 映射文件的加载
【例子】下面是一个使用mapper代理的例子。这个例子将承接3.1小结的内容。
创建Mapper接口
对于要管理的Xxx表,我们已经有了Xxx映射SQL文件。现在创建XxxMapper接口。一般这个接口放在mapper包里面,便于管理。
为了使这两个文件(编译后)在同一目录下,将UserMapper.xml文件在resources文件夹下的路径,和UserMapper.java文件在java文件下的路径一致。(resources文件夹和java文件夹在编译之后将会放在同一文件夹classes下)
在resources文件夹下创建文件夹,使之路径与mapper文件夹保持一致。
![]()
注意 创建文件夹的时候注意用 / 分隔,而不能使用 . 分隔,使用/ 分隔会形成多层文件夹,而使用 . 分隔会仅仅创建以 . 命名的文件夹。
然后将UserMapper.xml移到刚刚创建好的文件夹下。
![]()
编译好的项目将会放在target文件夹中。编译项目后查看target文件夹,将会发现此时由UserMapper.java编译成的UserMapper.class和UserMapper.xml文件已经处于同一目录下。
![]()
最后需要注意修改mybatis核心配置文件mybatis-config.xml文件中mappers块中指定的映射文件路径。
设置SQL映射文件的namespace属性
将其设置为Mapper接口全限定名。
在Mapper接口中定义方法
方法名就是SQL映射文件的sql语句id,参数类型和返回值类型需要保持一致。
编码
public class App2mapper { public static void main( String[] args ) throws Exception { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); //获取UserMapper接口的代理对象 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); List<User> users = userMapper.selectAll(); System.out.println(users); //释放资源 sqlSession.close(); } }
输入结果
包扫描的形式配置sql映射文件
在使用mapper代理的形式下,sql映射文件都放在mapper文件夹中,可以在核心文件中mappers的使用包扫描形式一次性加载包下所有映射文件。
<package name="org.example.mapper"/>
*别名映射
注意 该实体类的成员变量名需要和数据库表中列名保持一致,否则无法自动封装。
如果不一致,可以在sql语句查询的时候使用别名。如
select user_Name as userName from tb_user;
或者在sql映射文件中使用resultMap映射
resultMap的子标签有两种:
- id:完成主键字段的映射
- result:完成一般字段的映射
<resultMap id="brandResultMap" tpye="brand">
<result column="user_name" property="userName" />
</resultMap>
3.3 核心配置文件
官方文档很详细MyBatis 3 | Configuration – mybatis
3.3.1顶层结构
注意 配置各个标签的时,需要遵守前后顺序,顺序如上图。
3.3.2 environments
配置要连接的数据库。可以配置多个,在使用的时候修改environments标签中的default属性即可灵活切换要使用的数据源。
3.3.3 tpyeAliases 类型别名
在恰当的位置添加如下语句,在sql映射文件中使用org.example.pojo包下的类就不用带包名,直接使用类名即可。

3.4 MyBatis案例
3.4.1 环境准备
该例子承接上面的例子。如果从0开始构建项目只需要导入必要的依赖、构建项目结构、配置核心文件的数据源,即environments即可。具体参考3.1快速入门的内容。
项目结构
![]()
项目结构 ![]()
必要依赖 ![]()
配置数据源 创建数据库以及对应表
-- 如果没有数据库先创建数据库 use mydb; drop table if exists tb_brand; -- 创建tb_brand表 create table tb_brand ( -- id 主键 id int primary key auto_increment, -- 品牌名称 brand_name varchar(20), -- 企业名称 company_name varchar(20), -- 排序字段 ordered int, -- 描述信息 description varchar(100), -- 状态:0:禁用 1:启用 status int ); -- 添加数据 insert into tb_brand (brand_name, company_name, ordered, description, status) values ('三只松鼠', '三只松鼠股份有限公司', 5, '好吃不上火', 0), ('华为', '华为技术有限公司', 100, '华为致力于把数字世界带入每个人、每个家庭、每个组织,构建万物互联的智能世界', 1), ('小米', '小米科技有限公司', 50, 'are you ok', 1); SELECT * FROM tb_brand;
创建pojo包,在该包下面创建实体类。根据刚刚创建的表结构创建对应的实体类Brand
package org.example.pojo; public class Brand { //主键 private Integer id ; //品牌名称 private String brand_name ; //公司名称 private String company_name; //排序字段 private Integer ordered ; //描述信息 private String description ; //状态【1启用 0禁用】 private Integer status ; //下面还包含所有属性的getter和setter方法,以及该类的toString方法 }
创建测试文件
文件结构如图
安装MyBatisX插件,该插件能够帮助我们更高效使用MyBatis框架。
主要功能:XML和接口方法相互跳转;根据接口生成statement(核心文件中的mapper映射)
假如在对应jopo类中创建一个方法,该方法对应sql语句在mapper配置中没有配置,将可以快捷生成对应的sql语句。
自动生成sql语句之后,补全select块内的内容
3.4.2 查询所有数据
这一步和上面的例子一致,不做过多赘述。
步骤:
- 编写接口方法:Mapper接口 List<Brand> selectAll();
- 参数:无
- 结果:List<Brand>
- 编写SQL语句:SQL映射文件 <select id="selectAll" resultType="brand"> select * from tb_brand; </ select>
- 执行方法,测试
3.4.3 单条件查询
步骤:
- 编写接口方法:Mapper接口 List<Brand> selectAll();
- 参数:int id
- 结果:List<Brand>
- 编写SQL语句:SQL映射文件 <select id="selectAll" resultType="brand"> select * from tb_brand; </ select>
- 执行方法,测试
*参数占位符
mybatis中有两种参数占位符:
- #{} :会将sql语句对应地方替换为 ?占位符,防止sql注入
- ${} :直接拼接成sql语句,存在sql注入问题
- 使用时机:参数传递的时候使用#{} ;表名或者列名不确定的情况下才使用 ${}。
参数类型:
- parameterType:设置参数类型,该参数可以省略。
SQL语句中特殊字符处理(如小于号 <)
- 使用转义字符,如<为 <
- CDATA区
对于sql语句select * from tb_brand where id < #{id};
<!--方法一:使用转义字符-->
<select id="selectById" resultType="org.example.pojo.Brand">
select * from tb_brand where id < #{id};
</select>
<!--方法二:使用CDATA区-->
<select id="selectById" resultType="org.example.pojo.Brand">
select * from tb_brand where id
<![CDATA[
<
]]>
#{id};
</select>
【例子】
定义接口方法
编写映射文件
3.4.4 多条件查询的参数封装
首先配置sql映射文件
这里面使用 #{} 参数占位符的形式。
<select id="selectByCondition" resultType="org.example.pojo.Brand">
select * from tb_brand
where status = #{status}
and company_name like #{companyName}
and brand_name like #{brandName}
</select>
定义接口的时候,需要传递多个参数。参数的封装方式有三种:
法一:散装参数(使用@Param)
定义接口方法
//定义接口方法 public interface BrandMapper { //多条件查询 //法1 散装参数:如果方法中有多个参数,需要使用@Param("参数名")注解 public List<Brand> selectByCondition( @Param("status") int status, @Param("companyName") String companyName, @Param("brandName") String brandName ); }
注意,上面@Param注解里的参数名应与sql映射文件中 #{} 里的参数名对应。
使用该方法
public void testSelectByCondition() throws Exception { //设置参数 int status=1; String companyName="华为"; String brandName="华为"; //处理参数 模糊查询 companyName="%"+companyName+"%"; brandName="%"+brandName+"%"; //获取SqlSessionFactory对象 String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class); //执行方法 List<Brand> brands=brandMapper.selectByCondition(status, companyName, brandName); System.out.println(brands); //释放资源 sqlSession.close(); }
补充知识:
其实这种参数传递方法,MyBatis实际上是将这一个个需要传递的参数封装进一个Map容器。@Param注解里的参数将会称为键值对中的键名。Map中的键将会与映射文件里的#{}参数对应。如果不使用@Param注解,键名将会是默认值arg0,arg1……或者param1,param2……因此,如果不适用@Param注解,将映射文件中的#{}参数改成arg0,arg1……或者param1,param2……也能传递参数。
不过这种方式并不推荐。
法二:对象参数
获取参数之后,需要将参数封装成对应的实体类对象。
定义接口方法
//定义接口方法 public interface BrandMapper { //多条件查询 //法2 对象参数 public List<Brand> selectByCondition(Brand brand); }
注意 对象的属性名称要和参数占位符名称一致。
如本例子中参数占位符#{}里面的参数名分别是status、companyName、brandName,需要保证实体类参数名是一致的。(为了一致我这里对实体类属性名稍作修改)
使用该方法
public void testSelectByCondition() throws Exception { //设置参数 int status=1; String companyName="华为"; String brandName="华为"; companyName="%"+companyName+"%"; brandName="%"+brandName+"%"; //封装成实体类 Brand brand=new Brand(); brand.setStatus(status); brand.setCompanyName(companyName); brand.setBrandName(brandName); //获取SqlSessionFactory对象 String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class); //执行方法 List<Brand> brands=brandMapper.selectByCondition(brand); System.out.println(brands); //释放资源 sqlSession.close(); }
法三:map参数
定义接口方法
//定义接口方法 public interface BrandMapper { //多条件查询 //法3 map参数 public List<Brand> selectByConditionSingle(Map map); }
使用该方法
需要将参数封装成Map,其中封装好的Map注意键名需要和sql映射配置中的 #{} 内的参数名一致。
public void testSelectByCondition() throws Exception { //设置参数 int status=1; String companyName="华为"; String brandName="华为"; companyName="%"+companyName+"%"; brandName="%"+brandName+"%"; //封装成Map Map map=new HashMap(); map.put("status",status); map.put("companyName",companyName); map.put("brandName",brandName); //获取SqlSessionFactory对象 String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); //获取mapper接口对象 BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class); //执行方法 List<Brand> brands=brandMapper.selectByCondition(map); System.out.println(brands); //释放资源 sqlSession.close(); }
小结
参数传递底层原理
笔者认为这部分内容初学者无需理解,如果要详细学习建议看黑马课程p13.
黄色框里面红字的部分是参数被封装成Map时,键值对默认的键名。
在映射文件获取这些封装的参数,如果不使用@Param注解,直接使用默认键名(如“arg0”、“collection”)也能获取传递的参数。
3.4.5 动态条件查询
即查询的条件不固定。有时候可能需要对status进行筛选,有时候不对status进行筛选。
<if>标签
<if>标签是myBatis提供的标签,当<if>内test属性内的语句为正的时候才会将<if>标签包裹的语句添加到查询语句里面。下面是一个动态查询示例。
注意 为了满足语法,where后面跟着恒等式1=1。
<select id="selectByCondition" resultType="org.example.pojo.Brand">
select * from tb_brand
where 1=1
<if test="status != null">
and status = #{status}
</if>
<if test="companyName != null">
and company_name like #{companyName}
</if>
<if test="brandName != null">
and brand_name like #{brandName}
</if>
</select>
<where>标签
myBatis提供的标签。当
<where>
标签内的<if>
等条件标签有条件满足时,会自动添加 WHERE 关键字到 SQL 语句中;若<where>
标签后的条件语句以 AND 或 OR 开头,它会自动剔除该开头的 AND 或 OR ,确保 WHERE 子句语法正确。
上面例子中,为了确保sql语句语法正确,必须在where后面跟恒等式。以下代码中,将where替换成<where>,即使没有恒等式也没有关系。myBatis会自动将关键字“and”进行保留或者删除以满足语法。
<select id="selectByCondition" resultType="org.example.pojo.Brand">
select * from tb_brand
<where>
<if test="status != null">
and status = #{status}
</if>
<if test="companyName != null">
and company_name like #{companyName}
</if>
<if test="brandName != null">
and brand_name like #{brandName}
</if>
</where>
</select>
<choose><when><otherwise> 标签
这三个标签配合使用类似于Java中的switch、case、default关键字。用于多个条件中选择一个条件进行查询。
<choose>
:是父标签,作为容器,按顺序判断内部子标签条件。<when>
:<choose>
的子标签,可包含一个或多个。通过test
属性设置条件表达式,当表达式值为true
时,执行<when>
内的 SQL 语句片段,且<choose>
结束判断 ,不再检查后续<when>
及<otherwise>
。例如<when test="status == 1"> AND status = #{status} </when>
。<otherwise>
:是<choose>
的可选子标签,最多只能有一个,且须在所有<when>
标签之后。当前面所有<when>
的条件都不满足时,执行其内部 SQL 语句片段。如<otherwise> AND is_deleted = 0 </otherwise>
。
以下例子中,只有一个<when>里面的sql语句会被使用。
<select id="selectByConditionSingle" resultMap="brandResultMap"> select * from tb_brand where <choose> <!--类似于switch--> <when test="status != null"> <!--类似于case--> status = #{status} </when> <when test="companyName != null and companyName != """> company_name like #{companyName} </when> <when test="brandName != null and brandName != " "> brand_name like #{brandName} </when> <otherwise> <!--类似于default--> 1 = 1 </otherwise> </choose> </select>
3.4.6 添加
定义方法接口
public interface BrandMapper { //添加 public void add(Brand brand); }
配置sql映射
注意 #{} 内参数名应该与实体类对应属性名保持一致
<insert id="add"> insert into tb_brand (brand_name,company_name,ordered,description,status) values (#{brand_name},#{company_name},#{ordered},#{description},#{status}); </insert>
使用方法
将需要用到的参数封装成实体类。
注意 MyBatis 默认开启手动提交事务模式,执行
INSERT
、UPDATE
、DELETE
后需手动提交。即需要执行commit()public void testAdd() throws Exception { //设置参数 int status=1; String companyName="小样手机"; String brandName="小样"; String description="这手机真好用"; int ordered=100; //封装成实体类对象 Brand brand=new Brand(); brand.setStatus(status); brand.setCompany_name(companyName); brand.setBrand_name(brandName); brand.setDescription(description); brand.setOrdered(ordered); //获取SqlSessionFactory对象 String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class); //执行方法 brandMapper.add(brand); //提交事务 sqlSession.commit(); //释放资源 sqlSession.close(); }
获取返回的id
如果插入数据后需要获取自增长的主键值,可以使用insert标签里面的useGenerateKeys和KeyProperty属性。
- useGenerateKeys:取值true或者false。指示 MyBatis 是否使用 JDBC 的
getGeneratedKeys
方法来获取数据库表自动生成的主键值,比如常见的自增长主键 。 - KeyProperty:指定将数据库表自动生成的主键值赋值给传入的 Java 对象的哪个属性 。
<insert id="add" useGeneratedKeys="true" keyProperty="id"> insert into tb_brand (brand_name,company_name,ordered,description,status) values (#{brand_name},#{company_name},#{ordered},#{description},#{status}); </insert>
设置之后,如下代码执行完之后,封装的对象brand内id属性将会被填充。从而获取添加数据后,该行的id。
public void testAdd() throws Exception { //设置参数 int status=1; String companyName="小样手机"; String brandName="小样"; String description="这手机真好用"; int ordered=100; //封装成实体类对象 Brand brand=new Brand(); brand.setStatus(status); brand.setCompany_name(companyName); brand.setBrand_name(brandName); brand.setDescription(description); brand.setOrdered(ordered); //获取SqlSessionFactory对象 String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class); //执行方法 brandMapper.add(brand); //提交事务 sqlSession.commit(); //获取新增后的id System.out.println(brand.getId()); //释放资源 sqlSession.close(); }
3.4.7 修改字段
定义接口方法
public interface BrandMapper { //修改 返回值可以是void。如果返回值是int类型,将会返回受影响的行数 public int update(Brand brand); }
配置映射
<update id="update"> update tb_brand set brand_name = #{brand_name}, company_name = #{company_name}, ordered = #{ordered}, description = #{description}, status = #{status} where id = #{id}; </update>
执行方法
public void testUpdate() throws Exception { //设置参数 int status=1; String companyName="晓燕手机"; String brandName="晓燕"; String description="这晓燕手机真好用"; int ordered=100; int id=8; //封装成实体类对象 Brand brand=new Brand(); brand.setStatus(status); brand.setCompany_name(companyName); brand.setBrand_name(brandName); brand.setDescription(description); brand.setOrdered(ordered); brand.setId(id); //获取SqlSessionFactory对象 String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class); //执行方法,获取受影响的行数 int count=brandMapper.update(brand); //提交事务 sqlSession.commit(); //打印受影响的行数 System.out.println(count); //释放资源 sqlSession.close(); }
修改动态字段
某些字段可能修改,也可能不修改,使用<set>和<if>实现动态修改。
<set>
标签
- 专门用于动态生成 SQL 的
UPDATE
语句中的SET
子句。 - 自动处理逗号问题:会自动剔除多余的逗号(
,
),避免 SQL 语法错误。
<if>
标签
- 实现条件判断,类似 Java 中的
if
语句。 - 根据传入参数的值动态决定是否包含某段 SQL 片段。
sql映射文件
<update id="update"> update tb_brand <set> <if test="brandName != null and brandName != ' '"> brand_name = #{brandName}, </if> <if test="companyName != null and companyName != ''"> company_name = #{companyName}, </if> <if test="ordered != null"> ordered = #{ordered}, </if> <if test="description != null and description != ''"> description = #{description}, </if> <if test="status != null"> status = #{status}, </if> </set> where id = #{id}; </update>
3.4.8 删除
删除单个
定义接口方法
public interface BrandMapper { //根据id删除 public void deleteById(int id); }
配置映射
<delete id="deleteById"> delete from tb_brand where id = #{id}; </delete>
执行方法
public void testDeleteById() throws Exception { //设置参数 int id=8; //获取SqlSessionFactory对象 String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class); //执行方法 brandMapper.deleteById(id); //提交事务 sqlSession.commit(); //释放资源 sqlSession.close(); }
删除多个(使用<foreach>)
接受的参数是一个数组,在映射配置里面需要遍历数组获取需要删除的所有id。
在 MyBatis 中,<foreach>
标签用于遍历集合参数,生成动态 SQL。
定义接口方法
public interface BrandMapper { //根据id批量删除 public void deleteByIds(@Param("ids") int[] ids); }
配置映射
<delete id="deleteByIds"> delete from tb_brand where id in <foreach collection="ids" item="id" separator="," open="(" close=")"> #{id} </foreach> ; </delete>
- @Param("ids"):将方法参数 ids 绑定到 SQL 映射中的 #{ids}。
- 若不使用 @Param,MyBatis 会默认将数组参数命名为 "array",此时 <foreach> 的 collection 需改为 "array"。
- separator 属性:定义每次迭代后添加的分隔符,用于连接集合中的多个元素。
- open 属性:定义动态生成的 SQL 片段的起始符号,通常与 close 配合使用,用于包裹整个集合元素的拼接结果。
- close 属性 :定义动态生成的 SQL 片段的结束符号,与 open 对应。
使用方法
public void testDeleteByIds() throws Exception { //设置参数 int ids[]={3,9,10}; //获取SqlSessionFactory对象 String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class); //执行方法 brandMapper.deleteByIds(ids); //提交事务 sqlSession.commit(); //释放资源 sqlSession.close(); }
3.5 使用注解进行增删改查
对于简单的sql语句,使用注解的形式会比使用配置文件更加方便、简洁。
- 查询 @Select
- 添加 @Insert
- 修改 @Update
- 删除 @Delete
下面是一个简单的例子说明使用@Select注解如何配置。其他操作操作类似。
使用注解配置对应sql映射之后,就无需在映射文件中进行sql映射配置了。
public interface UserMapper { @Select("select * from tb_user") List<User> selectAll(); @Select("select * from tb_user where id= #{id} ") User selectById(@Param("id") int id); }