目录
JavaWeb
MySql基础篇:MySql学习笔记(语法速查,快速入门,MySQL干货,持续更新~)_aim_v的博客-CSDN博客
一、JDBC
1.JDBC快速入门
JDBC( Java DataBase Connectivity )就是使用Java语言操作关系型数据库的一套API
// 快速入门案例
//创建项目,导入驱动jar包
//1.注册驱动(jdk5版本以后可以省略)
Class.forName("com.mysql.cj.jdbc.Driver");
//数据库连接
String url = "jdbc:mysql://127.0.0.1:3306/test01";
String username = "root";
String password = "123456";
//获取连接
Connection conn = DriverManager.getConnection(url,username,password);
//定义SQL语句
String sql = "update emp set name = '张三' where id = 6";
//获取执行sql对象
Statement stmt = conn.createStatement();
//执行sql
int count = stmt.executeUpdate(sql);
//处理结构
//System.out.println(count);
//释放资源
stmt.close();
conn.close();
2.JDBC-API详解
DriverManager
(1)DriverManager(驱动管理类)作用:
1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
Driver源码
注:
MySQL 5 之后的驱动包,可以省略注册驱动的步骤
自动加载jar包中META-INF/services/java.sql.Driver文件中的驱动类
2.获取数据库连接
static Connection getConnection(String url,String user,String password)
参数
1.url:连接路径
语法:jdbc:mysql://ip地址(域名):端口号/数据库名称?参数键值对1&参数键值对2...
示例:jdbc:mysql://127.0.0.1:3306/db1
细节:
如果连接的是本机mysql服务器,并且mysql服务默认端口是3306,则url可以简写为:jdbc:mysql:///数据库名称?参数键值对
配置useSSL = false参数,禁用安全连接方式,解决警告提示
2.user:用户名
3.password:密码
Connection
(1)Connection(数据库连接对象)作用:
1.获取执行SQL的对象
普通执行SQL对象
Statement createStatement()
预编译SQL的执行SQL对象:防止SQL注入
PreparedStatement prepareStatement(sql)
执行存储过程的对象
CallableStatement prepareCall(sql)
2.管理事务
Mysql事务管理:
开启事务:BEGIN; / START TRANSACTION;
提交事务:COMMIT;
回滚事务:ROLLBACK;
--MySQL默认自动提交事务
JDBC事务管理:Connection接口中定义了3个对应的方法
开启事务:setAutoCommit ( boolean autoCommit )
(true为自动提交事务;false为手动提交事务,即开启事务)
提交事务:commint()
回滚事务:rollback()
//加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//数据库连接
String url = "jdbc:mysql://127.0.0.1:3306/test01";
String username = "root";
String password = "123456";
//获取连接
Connection conn = DriverManager.getConnection(url, username, password);
//定义SQL语句
String sql1 = "update emp set name = '张三' where id = 6";
String sql2 = "update emp set name = '李四' where id = 5";
//获取执行sql对象
Statement stmt = conn.createStatement();
try {
//开启事务
conn.setAutoCommit(false);
//执行sql
int count1 = stmt.executeUpdate(sql1);
//处理结构
System.out.println(count1);
int i = 3 / 0;
//执行sql
int count2 = stmt.executeUpdate(sql2);
//处理结构
System.out.println(count2);
//提交事务
conn.commit();
} catch (Exception e) {
//回滚事务
conn.rollback();
throw new RuntimeException(e);
}
//释放资源
stmt.close();
conn.close();
Statement
(1)Statement作用:
1.执行SQL语句
int executeUpdate(sql)
:执行DML、DDL语句
返回值:(1)DML语句影响行数(2)DDL语句执行后,执行成功也可能返回0
ResultSet executeQuery(sql)
:执行DQL语句
返回值:ResultSet结果集对象
/**
* 执行DML语句
* @throws Exception
*/
@Test
public void testDML() throws Exception {
//加载驱动
//Class.forName("com.mysql.cj.jdbc.Driver");
//数据库连接
String url = "jdbc:mysql://127.0.0.1:3306/test01";
String username = "root";
String password = "123456";
//获取连接
Connection conn = DriverManager.getConnection(url,username,password);
//定义SQL语句
String sql = "update emp set name = '张三' where id = 6";
//获取执行sql对象
Statement stmt = conn.createStatement();
//执行sql
int count = stmt.executeUpdate(sql);
//处理结构
if (count > 0){
System.out.println("修改成功");
}else{
System.out.println("修改失败");
}
//释放资源
stmt.close();
conn.close();
}
/**
* 执行DDL语句
* @throws Exception
*/
@Test
public void testDDL() throws Exception {
//加载驱动
//Class.forName("com.mysql.cj.jdbc.Driver");
//数据库连接
String url = "jdbc:mysql://127.0.0.1:3306/test01";
String username = "root";
String password = "123456";
//获取连接
Connection conn = DriverManager.getConnection(url,username,password);
//定义SQL语句
String sql = "create database db2";
//获取执行sql对象
Statement stmt = conn.createStatement();
//执行sql
int count = stmt.executeUpdate(sql);
//处理结构
System.out.println(count);
//释放资源
stmt.close();
conn.close();
}
ResultSet
(1)ResultSet(结果集对象)作用:
1.封装了DQL查询语句的结果
ResultSet stmt.executeQuery(sql)
(2)获取查询结果
boolean next()
:(1)将光标从当前位置向前移动一行(2)判断当前行是否为有效行
返回值:
true:有效行,当前行有数据
false:无效行,当前行没有数据
xxx getXxx(参数)
:获取数据
xxx:数据类型;如:int getInt(参数);String getString(参数)
参数:
int:列的编号,从1开始
String:列的名称
(3)使用
//定义Account实体类(在数据库中创建对应表),属性如下
private int id;
private String name;
private double money;
/**
* ResultSet
*
* @throws Exception
*/
@Test
public void testDML() throws Exception {
//加载驱动
//Class.forName("com.mysql.cj.jdbc.Driver");
//数据库连接
String url = "jdbc:mysql://127.0.0.1:3306/db1";
String username = "root";
String password = "123456";
//获取连接
Connection conn = DriverManager.getConnection(url, username, password);
//定义SQL语句
String sql = "select * from user";
//获取执行sql对象
Statement stmt = conn.createStatement();
//获取结果集
ResultSet rs = stmt.executeQuery(sql);
//用来存放结果
List<Account> list = new ArrayList<>();
//遍历结果集
while (rs.next()) {
Account account = new Account();
int id = rs.getInt("id"); //和数据库中的列名保持一致
String name = rs.getString("name");
double sal = rs.getDouble("sal");
account.setId(id);
account.setName(name);
account.setMoney(sal);
list.add(account);
}
System.out.println(list);
//释放资源
rs.close();
stmt.close();
conn.close();
}
PreparedStatement
(1)PreparedStatement作用:
1.预编译SQL语句并执行--预防SQL注入问题
(2)SQL注入
SQL注入是通过操作输入来修改事先定义好的SQL语句,用以达到执行代码对服务器进行攻击的方法
//SQL注入演示
/**
* SQL注入
*
* @throws Exception
*/
@Test
public void testLogin() throws Exception {
//数据库连接
String url = "jdbc:mysql://127.0.0.1:3306/db1";
String username = "root";
String password = "123456";
//获取连接
Connection conn = DriverManager.getConnection(url, username, password);
String name = "zhangsan";
String pwd = "' or '1' = '1";
//定义SQL语句
String sql = "select * from user where username = '" + name + "' and password = '"+pwd+"'";
//获取执行sql对象
Statement stmt = conn.createStatement();
//获取结果集
ResultSet rs = stmt.executeQuery(sql);
if (rs.next()){
System.out.println("登陆成功");
}else {
System.out.println("登陆失败");
}
//释放资源
rs.close();
stmt.close();
conn.close();
}
(3)使用
1.获取PreparedStatement对象
//SQL语句中的参数值,使用?占位符替代
String sql = "select * from user where username = ? and password = ?";
//通过Connection对象获取,并传入对应的sql语句
PreparedStatement pstmt = conn.prepareStatement(sql);
2.设置参数值
PreparedStatement对象:setXxx(参数1,参数2):给?赋值
Xxx:数据类型;如setInt(参数1,参数2)
参数:
参数1:?的位置编号,从1开始
参数2:?的值
3.执行SQL
executeUpdate(); / executeQuery(); : 不需要再传递sql
/**
* PreparedStatement使用
*
* @throws Exception
*/
@Test
public void testLogin() throws Exception {
//数据库连接
String url = "jdbc:mysql://127.0.0.1:3306/db1";
String username = "root";
String password = "123456";
//获取连接
Connection conn = DriverManager.getConnection(url, username, password);
//用户输入的账号和密码
String name = "zhangsan";
String pwd = "123";
//定义SQL语句
String sql = "select * from user where username = ? and password = ?";
//获取执行sql对象
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1,name);
pstmt.setString(2,pwd);
//获取结果集
ResultSet rs = pstmt.executeQuery();
if (rs.next()){
System.out.println("登陆成功");
}else {
System.out.println("登陆失败");
}
//释放资源
rs.close();
pstmt.close();
conn.close();
}
注:
使用PreparedStatement预编译SQL语句,性能更高,需要先在参数键值对中添加userServerPrepStmts开启预编译功能。
3.数据库连接池
数据库连接池简介
-
数据库连接池是个容器,负责分配、管理数据库连接
-
它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个
-
释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接二引起的数据库连接遗漏
-
好处:
-
资源重用
-
提升系统响应速度
-
避免数据库资源遗漏
-
数据库连接池实现
-
标准接口:DataSourcce
-
官方提供的数据库连接池标准接口,由第三方组织实现此接口
-
功能:获取连接
Connection getConnection()
-
-
常见的数据库连接池:
-
DBCP
-
C3P0
-
Druid
-
-
Druid(德鲁伊)
-
Druid连接池是阿里巴巴开源的数据库连接池项目
-
功能强大,性能优秀,是Java语言最好的数据库连接池之一
-
Druid使用步骤
-
导入jar包:下载地址(1.2.15版本):https://repo1.maven.org/maven2/com/alibaba/druid/1.2.15/druid-1.2.15.jar
-
定义配置文件(在当前目录结构中新建druid.properties文件)
# 定义配置文件
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql:///db1?useSSL=false&useServerPrepStmts=true
username=root
password=123456
# 初始化连接数量
initialSize=5
# 最大连接数
maxActive=10
# 最大等待时间
maxWait=3000
-
加载配置文件---获取连接池对象---获取数据库连接
public static void main(String[] args) throws Exception {
//3.加载配置文件
Properties prop = new Properties();
prop.load(new FileInputStream("src/com/zq/javaweb/druid.properties"));
//获取连接池对象
DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);
//获取数据库连接
Connection conn = dataSource.getConnection();
System.out.println(conn);
}
druid配置详解
属性 | 说明 | 建议值 |
---|---|---|
url | 数据库的jdbc连接地址。一般为连接oracle/mysql。示例如下: mysql : jdbc:mysql://ip:port/dbname?option1&option2&… oracle : jdbc:oracle:thin:@ip:port:oracle_sid | |
username | 登录数据库的用户名 | |
password | 登录数据库的用户密码 | |
initialSize | 启动程序时,在连接池中初始化多少个连接 | 10-50已足够 |
maxActive | 连接池中最多支持多少个活动会话 | |
maxWait | 程序向连接池中请求连接时,超过maxWait的值后,认为本次请求失败,即连接池没有可用连接,单位毫秒,设置-1时表示无限等待 | 100 |
minEvictableIdleTimeMillis | 池中某个连接的空闲时长达到 N 毫秒后, 连接池在下次检查空闲连接时,将回收该连接,要小于防火墙超时设置net.netfilter.nf_conntrack_tcp_timeout_established的设置 | 见说明部分 |
timeBetweenEvictionRunsMillis | 检查空闲连接的频率,单位毫秒, 非正整数时表示不进行检查 | |
keepAlive | 程序没有close连接且空闲时长超过 minEvictableIdleTimeMillis,则会执行validationQuery指定的SQL,以保证该程序连接不会池kill掉,其范围不超过minIdle指定的连接个数。 | true |
minIdle | 回收空闲连接时,将保证至少有minIdle个连接. | 与initialSize相同 |
removeAbandoned | 要求程序从池中get到连接后, N 秒后必须close,否则druid 会强制回收该连接,不管该连接中是活动还是空闲, 以防止进程不会进行close而霸占连接。 | false,当发现程序有未正常close连接时设置为true |
removeAbandonedTimeout | 设置druid 强制回收连接的时限,当程序从池中get到连接开始算起,超过此值后,druid将强制回收该连接,单位秒。 | 应大于业务运行最长时间 |
logAbandoned | 当druid强制回收连接后,是否将stack trace 记录到日志中 | true |
testWhileIdle | 当程序请求连接,池在分配连接时,是否先检查该连接是否有效。(高效) | true |
validationQuery | 检查池中的连接是否仍可用的 SQL 语句,drui会连接到数据库执行该SQL, 如果正常返回,则表示连接可用,否则表示连接不可用 | |
testOnBorrow | 程序 申请 连接时,进行连接有效性检查(低效,影响性能) | false |
testOnReturn | 程序 返还 连接时,进行连接有效性检查(低效,影响性能) | false |
poolPreparedStatements | 缓存通过以下两个方法发起的SQL: public PreparedStatement prepareStatement(String sql) public PreparedStatement prepareStatement(String sql,int resultSetType, int resultSetConcurrency) | true |
maxPoolPrepareStatementPerConnectionSize | 每个连接最多缓存多少个SQL | 20 |
filters | 这里配置的是插件,常用的插件有: 监控统计: filter:stat 日志监控: filter:log4j 或者 slf4j 防御SQL注入: filter:wall | stat,wall,slf4j |
connectProperties | 连接属性。比如设置一些连接池统计方面的配置。 druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 比如设置一些数据库连接属性 |
增删改查代码示例
建表语句:
-- 删除tb_brand表
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;
java代码:
/**
* 查询所有
* SQL:select * from tb_brand;
* 参数:不需要
* 返回值:List<Brand>
* @throws Exception
*/
@Test
public void testSelectAll() throws Exception {
//加载配置文件
Properties prop = new Properties();
prop.load(new FileInputStream("src/com/zq/javaweb/druid.properties"));
//创建连接池对象
DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);
//获取数据库连接
Connection conn = dataSource.getConnection();
//定义SQL
String sql = "select * from tb_brand";
//定义SQL执行对象
PreparedStatement pstmt = conn.prepareStatement(sql);
//执行sql
ResultSet rs = pstmt.executeQuery();
Brand brand = null;
List<Brand> list = new ArrayList<>();
while (rs.next()){
int id = rs.getInt("id");
String brandName = rs.getString("brand_name");
String companyName = rs.getString("company_name");
int ordered = rs.getInt("ordered");
String description = rs.getString("description");
int status = rs.getInt("status");
brand = new Brand();
brand.setId(id);
brand.setBrandName(brandName);
brand.setCompanyName(companyName);
brand.setOrdered(ordered);
brand.setDescription(description);
brand.setStatus(status);
list.add(brand);
}
System.out.println(list);
//释放资源
rs.close();
pstmt.close();
conn.close();
}
/**
* 添加
* SQL:insert into tb_brand (brand_name,company_name,ordered,description,status) values (?,?,?,?,?)
* 参数:需要除id以外的所有数据
* 返回值:boolean
* @throws Exception
*/
@Test
public void testAdd() throws Exception {
//加载配置文件
Properties prop = new Properties();
prop.load(new FileInputStream("src/com/zq/javaweb/druid.properties"));
//创建连接池对象
DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);
//获取数据库连接
Connection conn = dataSource.getConnection();
//要提交的数据
String brandName = "香飘飘";
String companyName = "香飘飘";
int ordered = 1;
String description = "绕地球一圈";
int status = 1;
//定义SQL
String sql = "insert into tb_brand (brand_name,company_name,ordered,description,status) values (?,?,?,?,?)";
//定义SQL执行对象
PreparedStatement pstmt = conn.prepareStatement(sql);
//设置参数
pstmt.setString(1,brandName);
pstmt.setString(2,companyName);
pstmt.setInt(3,ordered);
pstmt.setString(4,description);
pstmt.setInt(5,status);
int count = pstmt.executeUpdate();
System.out.println(count > 0);
//释放资源
pstmt.close();
conn.close();
}
/**
* 根据id修改
SQL:update tb_brand set
brand_name = ?,
company_name = ?,
ordered = ?,
description = ?,
status = ?
where id = ?;
* 参数:需要所有数据
* 返回值:boolean
* @throws Exception
*/
@Test
public void testUpdate() throws Exception {
//加载配置文件
Properties prop = new Properties();
prop.load(new FileInputStream("src/com/zq/javaweb/druid.properties"));
//创建连接池对象
DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);
//获取数据库连接
Connection conn = dataSource.getConnection();
//要提交的数据
String brandName = "香飘飘";
String companyName = "香飘飘";
int ordered = 1000;
String description = "绕地球三圈";
int status = 1;
int id = 5;
//定义SQL
String sql = "update tb_brand set\n" +
" brand_name = ?,\n" +
" company_name = ?,\n" +
" ordered = ?,\n" +
" description = ?,\n" +
" status = ?\n" +
" where id = ?;";
//定义SQL执行对象
PreparedStatement pstmt = conn.prepareStatement(sql);
//设置参数
pstmt.setString(1,brandName);
pstmt.setString(2,companyName);
pstmt.setInt(3,ordered);
pstmt.setString(4,description);
pstmt.setInt(5,status);
pstmt.setInt(6,id);
int count = pstmt.executeUpdate();
System.out.println(count > 0);
//释放资源
pstmt.close();
conn.close();
}
/**
* 根据id删除
* SQL:delete from tb_brand where id = ?
* 参数:id
* 返回值:boolean
* @throws Exception
*/
@Test
public void testDelete() throws Exception {
//加载配置文件
Properties prop = new Properties();
prop.load(new FileInputStream("src/com/zq/javaweb/druid.properties"));
//创建连接池对象
DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);
//获取数据库连接
Connection conn = dataSource.getConnection();
//要提交的数据
int id = 5;
//定义SQL
String sql = "delete from tb_brand where id = ?";
//定义SQL执行对象
PreparedStatement pstmt = conn.prepareStatement(sql);
//设置参数
pstmt.setInt(1,id);
int count = pstmt.executeUpdate();
System.out.println(count > 0);
//释放资源
pstmt.close();
conn.close();
}
二、MyBatis
注:以下内容使用Maven进行项目构建,需要对Maven有一定了解
1.MyBatis快速入门
Mybatis简介
-
MyBatis是一款优秀的持久层(将数据保存的数据库的那一层代码)框架,用于简化JDBC开发
JavaEE三层架构:表现层、业务层、持久层
框架
-
框架是一个半成品软件,是一套可重用、通用的、软件基础代码模型
-
在框架的基础上构建软件编写更加高效、规范、通用、可扩展
使用步骤
-
创建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', '男', '西安');
-
创建模块,导入坐标
<!--在pom文件中添加以下内容--> <dependencies> <!--MyBatis 依赖--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.5</version> </dependency> <!-- mysql 驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.30</version> </dependency> <!-- junit 单元测试 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13</version> <scope>test</scope> </dependency> <!-- 添加slf4j日志api --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.20</version> </dependency> <!-- Logback是一个Java日志框架,是log4j项目的继承者,也是log4j创始人设计的另一个开源日志组件,性能比log4j要好。--> <!-- 添加logback-classic依赖 --> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency> <!-- 添加logback-core依赖 --> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-core</artifactId> <version>1.2.3</version> </dependency> </dependencies>
在src/main/resources中创建logback.xml文件,配置 Logback
<?xml version="1.0" encoding="UTF-8"?> <configuration> <!-- CONSOLE :表示当前的日志信息是可以输出到控制台的。 --> <appender name="Console" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>[%level] %blue(%d{HH:mm:ss.SSS}) %cyan([%thread]) %boldGreen(%logger{15}) - %msg %n</pattern> </encoder> </appender> <logger name="com.zq" level="DEBUG" additivity="false"> <appender-ref ref="Console"/> </logger> <!-- level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL和OFF,默认debug <root>可以包含零个或多个<appender-ref>元素,标识这个输出位置将会被本日志级别控制。 --> <root level="DEBUG"> <appender-ref ref="Console"/> </root> </configuration>
-
编写MyBatis核心配置文件(在src/main/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.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql:///mybatis?useSSL=false"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <mappers> <!--加载SQL映射文件--> <mapper resource="org/mybatis/example/BlogMapper.xml"/> </mappers> </configuration>
-
编写SQL映射文件(在src/main/resources中创建xxxMapper.xml文件,此处模拟对用户进行操作,故创建UserMapper.xml文件,并在pojo中新建User类)
<?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="com.zq.pojo.User"> <select id="selectAll" resultType="User"> select * from tb_user; </select> </mapper>
-
编码
-
定义pojo类
-
加载核心配置文件,获取SqlSessionFactory对象
-
获取SqlSession对象,执行SQL语句
-
释放资源
//加载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();
-
2.Mapper代理开发
使用步骤:
1.定义与SQL映射文件同名的Mapper接口,并且将Mapper接口和SQL映射文件放置在同一目录下
在resources目录下创建和Mapper接口同层级的目录,注意用/分隔,不能用.
2.设置SQL映射文件的namespace属性为Mapper接口全限定名
<mapper namespace="com.zq.pojo.User">
<select id="selectAll()" resultType="User">
select * from tb_user;
</select>
</mapper>
3.在Mapper 接口中定义方法,方法名就是SQL映射文件中sql语句的id,并保持参数类型和返回值类型一致
List<User> selectAll();
4.编码
1.通过 SqlSession的 getMapper方法获取 Mapper接口的代理对象
2.调用对应方法完成sql的执行
//加载mybatis的核心配置文件,获取SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//获取SqlSession对象,用来执行Sql语句
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> users = userMapper.selectAll();
System.out.println(users);
//释放资源
sqlSession.close();
3.MyBatis核心配置文件
MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。 配置文档的顶层结构如下:
-
configuration(配置)
-
-
environment(环境变量)
-
transactionManager(事务管理器)
-
dataSource(数据源)
-
-
注:配置各个标签时,要遵循以上前后顺序
4.MyBatis案例一(使用配置文件完成增删改查)
环境准备
1.在mybatis数据库中执行以下sql代码:
-- 删除tb_brand表
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;
2.创建实体类Brand
public class Brand {
// id 主键
private Integer id;
// 品牌名称
private String brandName;
// 企业名称
private String companyName;
// 排序字段
private Integer ordered;
// 描述信息
private String description;
// 状态:0:禁用 1:启用
private Integer status;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getBrandName() {
return brandName;
}
public void setBrandName(String brandName) {
this.brandName = brandName;
}
public String getCompanyName() {
return companyName;
}
public void setCompanyName(String companyName) {
this.companyName = companyName;
}
public Integer getOrdered() {
return ordered;
}
public void setOrdered(Integer ordered) {
this.ordered = ordered;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
@Override
public String toString() {
return "Brand{" +
"id=" + id +
", brandName='" + brandName + '\'' +
", companyName='" + companyName + '\'' +
", ordered=" + ordered +
", description='" + description + '\'' +
", status=" + status +
'}';
}
}
3.编写测试用例
在test目录下,新建com.xxx.test包,在包中新建MyBatisTest类
4.安装MyBatisX插件
在File-Settings-Plugins中搜索MyBatisX安装,安装后重启IDEA,可以看到小鸟的图标,点击小鸟可以跳转到对应的UserMapper接口或xml文件中
增删改查
1)查询所有数据
1.编写Mapper接口
//在mapper包下新建BrandMapper接口
/**
* 查询所有
* @return
*/
public interface BrandMapper {
List<Brand> selectAll();
}
2.编写xml文件
<!--在resources资源目录下的com.xxx.mapper中新建BrandMapper.xml文件-->
<?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">
<mapper namespace="org.zq.mapper.BrandMapper">
<!--
resultMap是为了解决数据库表的字段名称和实体类属性名称不一样的问题
字段名和属性名不同,则不能封装
除此之外,解决该问题的方法还有对列名起别名以及使用sql片段,让别名和实体类的属性名一样
-->
<resultMap id="BrandResultMap" type="Brand">
<result column="brand_name" property="brandName"/>
<result column="company_name" property="companyName"/>
</resultMap>
<select id="selectAll" resultMap="BrandResultMap">
select * from tb_brand
</select>
<!--使用起别名的方法-->
<!--
<select id="selectAll" resultMap="BrandResultMap">
select id, brand_name as brandName, company_name as companyName, ordered, description, status
from tb_brand
</select>
-->
<!--使用sql片段-->
<!--
<sql id="brand_column">
id, brand_name as brandName, company_name as companyName, ordered, description, status
</sql>
<select id="selectAll" resultType="Brand">
select <include refid="brand_column"/>
from tb_brand;
</select>
-->
</mapper>
3.在测试用例中执行方法
@Test
public void selectAll() throws IOException {
//加载mybatis的核心配置文件,获取SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//获取SqlSession对象,用来执行Sql语句
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取mapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
List<Brand> brands = brandMapper.selectAll();
System.out.println(brands);
//释放资源
sqlSession.close();
}
2)通过id查询
1.在BrandMapper接口中声明接口
/**
* 根据id查询
* @param id
* @return
*/
Brand selectById(int id);
2.编写sql语句
<!--
参数占位符:
1.#{}:会将其替换为?,可以防止sql注入
2.${}:会存在sql注入问题
使用时机:
参数传递的时候:#{}
表名或者列名不固定的情况下:${}
参数类型:parameterType:可以省略
特殊字符处理:
1.转义字符:< - <
2.CDATA区
-->
<select id="selectById" resultMap="BrandResultMap">
select * from tb_user where id = #{id};
</select>
<!-- <!–CDATA区–>
<select id="selectById" resultMap="BrandResultMap">
select * from tb_user where id
<![CDATA[
<
]]>
#{id};
</select>-->
3.在测试类中执行sql语句
@Test
public void selectAll() throws IOException {
//接收参数(模拟)
int id = 1;
//加载mybatis的核心配置文件,获取SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//获取SqlSession对象,用来执行Sql语句
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取mapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
Brand brand = brandMapper.selectById(id);
System.out.println(brand);
//释放资源
sqlSession.close();
}
3)条件查询
1.在BrandMapper接口中声明接口
/**
* 条件查询
* 1.散装参数
* 2.对象参数
* 3.map集合参数
* @param status
* @param companyName
* @param brandName
* @return
*/
List<Brand> selectByCondition(@Param("status")int status,@Param("companyName")String companyName,@Param("brandName")String brandName);
List<Brand> selectByCondition(Brand brand);
List<Brand> selectByCondition(Map map);
2.编写sql语句
<!-- 条件查询 -->
<select id="selectByCondition" resultMap="BrandResultMap">
select *
from tb_brand
where status = #{status}
and company_name like #{companyName}
and brand_name like #{brandName}
</select>
3.在测试类中执行sql语句
@Test
public void selectByCondition() throws IOException {
//接收参数(模拟)
int status = 1;
String companyName = "华为";
String brandName = "华为";
//参数处理
companyName = "%" + companyName + "%";
brandName = "%" + brandName + "%";
//加载mybatis的核心配置文件,获取SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//获取SqlSession对象,用来执行Sql语句
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取mapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//1.散装参数
List<Brand> list1 = brandMapper.selectByCondition(status,companyName,brandName);
//2.对象参数
Brand brand = new Brand();
brand.setStatus(status);
brand.setCompanyName(companyName);
brand.setBrandName(brandName);
List<Brand> list2 = brandMapper.selectByCondition(brand);
//3.map集合参数
Map map = new HashMap();
map.put("status",status);
map.put("companyName",companyName);
map.put("brandName",brandName);
List<Brand> list3 = brandMapper.selectByCondition(map);
System.out.println(list1);
//System.out.println(list2);
//System.out.println(list3);
//释放资源
sqlSession.close();
}
以上方法缺点:用户只根据单个或某几个条件查询时,其他条件的内容为null,此时查询不到想要的数据,需要通过动态条件查询优化
4)动态条件查询
动态sql标签:
-
if
-
choose (when, otherwise)
-
trim (where, set)
-
foreach
<!-- 多条件动态条件查询 -->
<!-- <where>标签是mybatis提供的用来去除逻辑运算符的标签(假设status为空,如果不加<where>标签,那么sql语句中where后会直接跟and,但是第一个条件不需要逻辑运算符,故需要去除) -->
<select id="selectByCondition" resultMap="BrandResultMap">
select *
from tb_brand
<where>
<if test="status != null">
status = #{status}
</if>
<if test="company_name != null and company_name != ''">
and company_name like #{companyName}
</if>
<if test="company_name != null and company_name != ''">
and brand_name like #{brandName}
</if>
</where>
</select>
<!--单条件动态查询-->
<select id="selectByConditionSingle" resultMap="BrandResultMap">
select *
from tb_brand
<where>
<choose><!--相当于switch-->
<when test="status != null">
status = #{status}
</when>
<when test="company_name != null and company_name != ''">
company_name like #{companyName}
</when>
<when test="company_name != null and company_name != ''">
brand_name like #{brandName}
</when>
</choose>
</where>
</select>
5)添加
1.编写Mapper接口(参数:除了id以外的所有数据 结果:void)
/**
* 插入数据
* @param brand
*/
void insert(Brand brand);
2.编写SQL语句,SQL映射文件
<!--插入-->
<!-- useGeneratedKeys="true" keyProperty="id" : 用来返回添加数据的主键 -->
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
insert into tb_brand
(id, brand_name, company_name, ordered, description, status)
values (#{brandName}, #{companyName}, #{ordered}, #{description}, #{status});
</insert>
3.执行方法
MyBatis事务:
openSession():默认开启事务,进行增删改查操作后需要使用sqlSession.commit();手动提交事务
openSession(true):可以设置为自动提交事务(关闭事务)
@Test
public void insert() throws IOException {
//接收参数(模拟)
int status = 1;
String companyName = "菠萝";
String brandName = "菠萝";
String description = "美国有苹果,中国有菠萝";
int ordered = 100;
//加载mybatis的核心配置文件,获取SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//获取SqlSession对象,用来执行Sql语句
SqlSession sqlSession = sqlSessionFactory.openSession(true);
//获取mapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//2.对象参数
Brand brand = new Brand();
brand.setStatus(status);
brand.setCompanyName(companyName);
brand.setBrandName(brandName);
brand.setDescription(description);
brand.setOrdered(ordered);
brandMapper.insert(brand);
Integer id = brand.getId();
System.out.println(id);
//释放资源
sqlSession.close();
}
6)修改
修改全部字段:
1.编写接口方法:Mapper接口(参数:所有数据 结果:void)
/**
* 修改
* @param brand
*/
int update(Brand brand);
2.编写SQL语句,SQL映射文件
<!--修改-->
<update id="update">
update tb_brand
set brand_name = #{brandName},
company_name = #{companyName},
ordered = #{ordered},
description = #{description},
status = #{status}
where id = #{id}
</update>
3.执行方法,测试
@Test
public void update() throws IOException {
//接收参数(模拟)
int id = 5;
int status = 1;
String companyName = "泡菜";
String brandName = "泡菜";
String description = "美国有苹果,中国有菠萝,韩国有泡菜";
int ordered = 50;
//加载mybatis的核心配置文件,获取SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//获取SqlSession对象,用来执行Sql语句
SqlSession sqlSession = sqlSessionFactory.openSession(true);
//获取mapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//2.对象参数
Brand brand = new Brand();
brand.setStatus(status);
brand.setCompanyName(companyName);
brand.setBrandName(brandName);
brand.setDescription(description);
brand.setOrdered(ordered);
brand.setId(id);
int column = brandMapper.update(brand);
System.out.println(column);
//释放资源
sqlSession.close();
}
修改动态字段:
<!--修改SQL映射文件-->
<!--动态修改-->
<update id="update">
update tb_brand
<set>
<if test="brand_name != null and brand_name != ''">
brand_name = #{brandName},
</if>
<if test="brand_name != null and brand_name != ''">
company_name = #{companyName},
</if>
<if test="brand_name != null">
ordered = #{ordered},
</if>
<if test="brand_name != null and brand_name != ''">
description = #{description},
</if>
<if test="brand_name != null">
status = #{status}
</if>
</set>
where id = #{id}
</update>
7)删除
单个删除:
1.编写接口方法:Mapper接口(参数:id 结果:void)
/**
* 根据id删除
* @param id
*/
void deleteById(int id);
2.编写SQL语句,SQL映射文件
<!--根据id删除-->
<delete id="deleteById">
delete from tb_brand
where id = #{id}
</delete>
3.执行方法,测试
@Test
public void deleteById() throws IOException {
//接收参数(模拟)
int id = 5;
//加载mybatis的核心配置文件,获取SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//获取SqlSession对象,用来执行Sql语句
SqlSession sqlSession = sqlSessionFactory.openSession(true);
//获取mapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
brandMapper.deleteById(id);
//释放资源
sqlSession.close();
}
批量删除:
1.编写接口方法:Mapper接口(参数:id 结果:void)
/**
* 根据id批量删除
* @param ids
*/
void deleteByIds(@Param("ids") int[] ids);
2.编写SQL语句,SQL映射文件
<!--
mybatis会将数组参数封装为一个Map集合 array = 数组
需要在接口的参数类型处使用@Param注解改变map集合的默认key的名称
注意:如果不使用注解,collection只能设置为array
-->
<!--根据id批量删除-->
<delete id="deleteByIds">
delete from tb_brand
where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>;
</delete>
3.执行方法,测试
@Test
public void deleteByIds() throws IOException {
//接收参数(模拟)
int[] ids = {4,6,7};
//加载mybatis的核心配置文件,获取SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//获取SqlSession对象,用来执行Sql语句
SqlSession sqlSession = sqlSessionFactory.openSession(true);
//获取mapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
brandMapper.deleteByIds(ids);
//释放资源
sqlSession.close();
}
MyBatis参数传递
MyBatis底层对于接口方法中的参数进行不同的封装处理
单个参数:
1.POJO类型:直接使用,属性名和参数占位符名称一致
2.Map集合:直接使用,键名和参数占位符名称一致
3.Colletion:封装为Map集合
-
map.put("arg0",collection集合); map.put("collection",collection集合);
4.List:
-
map.put("arg0",list集合); map.put("collection",list集合); map.put("list",list集合);
5.Array:封装为Map集合
-
map.put("arg0",数组); map.put("array",数组);
6.其他参数:直接使用
多个参数:
封装为Map集合
默认封装以下内容:
map.put("arg0",参数值1);
map.put("param1",参数值1);
map.put("param2",参数值2);
map.put("arg1",参数值2);
MyBatis提供了ParamNameResolver类来进行参数封装
<!--如果接口中没有配置@Param注解-->
User select(String username,String password);
<!--xml文件中应该按照以下方式书写-->
<select id="select" resultType="User">
select *
from tb_user
where
username = #{arg0}
and password = #{arg1}
</select>
<!--不使用@Param,代码可读性很差,推荐使用@Param注解-->
5.MyBatis案例二(注解开发)
使用注解开发会比配置文件开发更加方便
-
查询:@Select
-
添加:@Insert
-
修改:@Update
-
删除:@Delete
使用注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂一点的语句,Java 注解不仅力不从心,还会让本就复杂的 SQL 语句更加混乱不堪。 因此,如果你需要做一些很复杂的操作,最好用 XML 来映射语句。
例:
//UserMapper
@Select("select * from tb_user where id = #{id}")
User selectById(int id);
//UserMapperTest
@Test
public void selectById() throws IOException {
int id = 1;
//加载mybatis的核心配置文件,获取SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.selectById(id);
System.out.println(user);
sqlSession.close();
}