今天从JDBC开始一点一点讲到MyBatis
JDBC
先给一个JDBC入门案例
第一步:注册驱动
Class.forName("com.mysql.jdbc.Driver");
第二步:获取连接
Connection conn = DriverManager.getConnection(url, username, password);
第三步:定义sql
String sql = “update…” ;
第四步:获取执行sql的对象
Statement stmt = conn.createStatement();
第五步:执行sql
int count=stmt.executeUpdate(sql); //返回受影响的行
第六步:释放资源
stmt.close();
conn.close();
JDBC API详解:
DriverManager(驱动管理类)作用:注册驱动,获取数据库连接
Driver类源码:
第一步:
随着Driver类的加载,自动注册DriverManger
在Mysql5之后不需要再自己加载Drvier类,导入Jar包时jar包中已经存储了该类的名称,会自动加载。
第二步:
参数
1. url:连接路径
语法:jdbc:mysql://ip地址(域名):端口号/数据库名称?参数键值对1&参数键值对2…
示例:jdbc:mysql://127.0.0.1:3306/db1 ?userSSL=false
细节:
如果连接的是本机mysql服务器,并且mysql服务默认端口是3306,则url可以简写为:jdbc:mysql:///数据库名称?参数键值对
配置 useSSL=false 参数,禁用安全连接方式,解决警告提示
2. user:用户名
3. password:密码
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为手动提交事务,即为开启事务
提交事务:commit()
回滚事务:rollback()
第一步:注册驱动
Class.forName("com.mysql.jdbc.Driver");
第二步:获取连接
Connection conn = DriverManager.getConnection(url, username, password);
第三步:定义sql
String sql = “update…” ;
第四步:获取执行sql的对象
Statement stmt = conn.createStatement();
try{
开启事务:
conn.setAutoCommit(false);
第五步:执行sql
int count=stmt.executeUpdate(sql); //返回受影响的行
提交事务:
conn.commit();
}
catch(Exception throwables){
conn.rollback();
}
第六步:释放资源
stmt.close();
conn.close();
Statement:
Statement作用:
1.执行SQL语句
执行SQL语句
int executeUpdate(sql):执行DML、DDL语句
返回值:(1) DML语句影响的行数 (2) DDL语句执行后,执行成功也可能返回 0
ResultSet executeQuery(sql):执行DQL 语句
返回值: ResultSet 结果集对象
ResultSet:
ResultSet(结果集对象)作用:
1.封装了DQL查询语句的结果
ResultSet stmt.executeQuery(sql):执行DQL 语句,返回 ResultSet 对象
获取查询结果
boolean next():(1) 将光标从当前位置向前移动一行 (2)判断当前行是否为有效行
返回值:
true:有效行,当前行有数据
false:无效行,当前行没有数据
xxx getXxx(参数):获取数据
xxx:数据类型;如:int getInt(参数) ; String getString(参数)
参数:
int:列的编号,从1开始
String:列的名称
//循环判断游标是否是最后一行末尾
while(rs.next()){
//获取数据
rs.getXxx(参数);
}
PreparedStatement
PreparedStatement作用:
1.预编译SQL语句并执行:预防SQL注入问题
•SQL注入
•SQL注入是通过操作输入来修改事先定义好的SQL语句,用以达到执行代码对服务器进行攻击的方法。
比如在登录时输入用户名,密码框中输入一个sql语句。'or'1'='1
此时查询语句为 SELET * FROM user WHERE username = '' AND password=''
将密码代入进查询语句,SELET * FROM user WHERE username = '' AND password=' 'or'1'='1'
此时该查询语句就能查询到所有信息。
于是就需要用PreparedStatement进行预编译SQL
用法:
①获取 PreparedStatement 对象
// SQL语句中的参数值,使用?占位符替代,因为之前使用的是拼接的方法,所以会导致SQL注入问题出现。
String sql = "select * from user where username = ? and password = ?";
// 通过Connection对象获取,并传入对应的sql语句
PreparedStatement pstmt = conn.prepareStatement(sql);
② 设置参数值
PreparedStatement对象:setXxx(参数1,参数2):给 ? 赋值
Xxx:数据类型 ; 如 setInt (参数1,参数2)
参数:
参数1: ?的位置编号,从1 开始
参数2: ?的值
③ 执行SQL
executeUpdate(); / executeQuery(); :不需要再传递sql
原因:会对传入的内容进行转义,如'or'1'='1会转为\'or\'1\'=\'1
PreparedStatement 原理
PreparedStatement 好处:
1.预编译SQL,性能更高
2.防止SQL注入:将敏感字符进行转义
①PreparedStatement 预编译功能开启:useServerPrepStmts=true
② 配置MySQL执行日志(重启mysql服务后生效)
log-output=FILE
general-log=1
general_log_file=“D:\mysql.log"
slow-query-log=1
slow_query_log_file=“D:\mysql_slow.log"
long_query_time=2
•PreparedStatement 原理:
1.在获取PreparedStatement对象时,将sql语句发送给mysql服务器进行检查,编译(这些步骤很耗时)
2.执行时就不用再进行这些步骤了,速度更快
3.如果sql模板一样,则只需要进行一次检查、编译
数据库连接池简介
•数据库连接池是个容器,负责分配、管理数据库连接(Connection)
•它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;
•释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏
•好处:
•资源重用
•提升系统响应速度
•避免数据库连接遗漏
数据库连接池实现
•标准接口:DataSource
•官方(SUN) 提供的数据库连接池标准接口,由第三方组织实现此接口。
Connection getConnection()
•功能:获取连接
•常见的数据库连接池:
•DBCP
•C3P0
•Druid
•Druid(德鲁伊)
•Druid连接池是阿里巴巴开源的数据库连接池项目
•功能强大,性能优秀,是Java语言最好的数据库连接池之一
MyBatis
MyBatis 是一款优秀的持久层框架,用于简化 JDBC 开发
MyBatis 本是 Apache 的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁
到了google code,并且改名为MyBatis 。2013年11月迁移到Github
官网:https://mybatis.org/mybatis-3/zh/index.html
持久层
负责将数据到保存到数据库的那一层代码
JavaEE三层架构:表现层(页面展示)、业务层(逻辑处理)、持久层
框架
框架就是一个半成品软件,是一套可重用的、通用的、软件基础代码模型
在框架的基础之上构建软件编写更加高效、规范、通用、可扩展
下面给大家讲讲JDBC的一个缺点
1.硬编码
注册驱动,获取连接
SQL 语句
2.操作繁琐
手动设置参数
手动封装结果集
//1. 注册驱动
Class.forName(“com.mysql.jdbc.Driver”);
//2. 获取Connection连接
String url = “jdbc:mysql:///db1?useSSL=false”;
String uname = “root”;
String pwd = “1234”;
Connection conn = DriverManager.getConnection(url, uname, pwd);
// 接收输入的查询条件
String gender = “男”;
// 定义sql
String sql = “select * from tb_user where gender = ?”;
// 获取pstmt对象
PreparedStatement pstmt = conn.prepareStatement(sql);
// 设置?的值
pstmt.setString(1,gender);
// 执行sql
ResultSet rs = pstmt.executeQuery();
// 遍历Result,获取数据
User user = null;
ArrayList<User> users = new ArrayList<>();
while (rs.next()){
//获取数据
int id = rs.getInt(“id”);
String username = rs.getString(“username”);
String password = rs.getString(“password”);
//创建对象,设置属性值
user = new User();
user.setId(id);
user.setUsername(username);
user.setPassword(password);
user.setGender(gender);
//装入集合
users.add(user);}
MyBatis将连接信息、SQL语句写到配置文件中
后两步可以直接一个语句完成:List<User> users=sqlSession.selectList("test.selectByGender","男")
MyBatis入门:
定义xml配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://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://127.0.0.1:3306/mayikt?serverTimezone=GMT%2B8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mybatis/userMaaper.xml"/>
</mappers>
</configuration>
Maaper文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="userMapper">
<select id="getByUsers" resultType="com.mayikt.entity.UserEntity">
select * from mayikt_users
</select>
</mapper>
测试代码
package com.mayikt.test;
import com.mayikt.entity.UserEntity;
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 java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class Test01 {
public static void main(String[] args) throws IOException {
// 1.读取加载mybatis-config.xml
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 2.获取到获取到
SqlSession sqlSession = sqlSessionFactory.openSession();
// 3.根据 mapper id=getByUsers 执行该s ql 语句 通过 sql语句得到我们的对象 orm
List<UserEntity> userEntitys = sqlSession.selectList("getByUsers", UserEntity.class);
System.out.println(userEntitys);
sqlSession.close();
}
}
解决SQL映射文件的警告提示
产生原因:Idea和数据库没有建立连接,不识别表信息
解决方式:在Idea中配置MySQL数据库连接
mapper代理开发模式
1.定义与SQL映射文件同名的Mapper接口,并且将Mapper接口和SQL映射文件放置在同一目录
2.设置SQL映射文件的namespace属性为Mapper接口全限定名
3.在 Mapper 接口中定义方法,方法名就是SQL映射文件中sql语句的id,并保持参数类型和返回值类型一致
4.编码
1.通过 SqlSession 的 getMapper方法获取 Mapper接口的代理对象
2.调用对应方法完成sql的执行
<mappers>
<mapper resource="mybatis/userMapper.xml"/>
</mappers>
package com.mayikt.mapper;
import com.mayikt.entity.UserEntity;
import java.util.List;
public interface UserMapper {
List<UserEntity> getByUsers();
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mayikt.mapper.UserMapper">
<select id="getByUsers" resultType="com.mayikt.entity.UserEntity">
select * from mayikt_users
</select>
</mapper>
测试类
package com.mayikt.test;
import com.mayikt.entity.UserEntity;
import com.mayikt.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 java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class Test01 {
public static void main(String[] args) throws IOException {
// 1.读取加载mybatis-config.xml
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 2.获取到获取到
SqlSession sqlSession = sqlSessionFactory.openSession();
// 3.根据 mapper id=getByUsers 执行该s ql 语句 通过 sql语句得到我们的对象 orm
// List<UserEntity> userEntitys = sqlSession.selectList("getByUsers", UserEntity.class);
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
System.out.println(mapper.getByUsers());
// System.out.println(userEntitys);
sqlSession.close();
}
}
MyBatis 核心配置文件的顶层结构如下:
类型别名(typeAliases)
<typeAliases >
<package name="com.mybatisd.pojo"/>
</typeAliases>
注意:
实体类属性名 和 数据库表列名 不一致,不能自动封装数据
1)起别名:在SQL语句中,对不一样的列名起别名,别名和实体类属性名一样
* 可以定义 <sql>片段,提升复用性
<sql id="brand_column">
id,brand_name as brandName,company_name as companyName
</sql>
<select>
select
<include refid="brand_column" />
from tb_brand;
</select>
缺点:不灵活
2)resultMap:定义<resultMap> 完成不一致的属性名和列名的映射(常用)
<!--
id:唯一标识
type:映射的类型,支持别名
-->
<resultMap id="brandResultMap" type="brand">
<!--
id:主键字段的映射
column:列名
property:实体类的属性名
result:完成一般字段的映射
-->
<result column="brand_name" property="brandName"></result>
<result column="company_name" property="companyName"></result>
</resultMap>
<select id="selectAll" resultMap="brandResultMap">
select
*
from tb_brand;
</select>
1. 参数占位符:
1)#{}:执行SQL时,会将#{}占位符替换为?,将来自动设置参数值
2)${}:拼SQL。会存在SQL注入问题
3) 使用时机:
* 参数传递,都使用#{}
* 如果要对表名、列名进行动态设置(表名或者列名不固定),只能使用${}进行sql拼接。
select * from ${} where id = #{}
2. parameterType:
* 用于设置参数类型,该参数可以省略
3. SQL 语句中特殊字符处理:
* 转义字符
* <![CDATA[ 内容 ]]>:CD提示
查询-多条件查询
1.编写接口方法: Mapper接口
参数:所有查询条件
结果:List<Brand>
2. 编写 SQL语句: SQL映射文件
3. 执行方法,测试
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);
<select id="selectByCondition" resultMap="brandResultMap">
select *
from tb_brand
where
status = #{status}
and company_name like #{companyName}
and brand_name like #{brandName}
</select>
SQL语句设置多个参数有几种方式?
1) 散装参数:需要使用@Param("SQL中的参数名称")
* 使用麻烦
2) 实体类封装参数
* 只需要保证SQL中的参数名 和 实体类属性名对应上,即可设置成功
3) map集合
* 只需要保证SQL中的参数名 和 map集合的键的名称对应上,即可设置成功
查询-多条件查询-动态条件
<select id="selectByCondition" resultMap="brandResultMap">
select *
from tb_brand
/*where 1=1*/
<where>
<if test="status !=null">
and status = #{status}
</if>
<if test="company_name!=null and company_name!=''">
and company_name like #{companyName}
</if>
<if test="brandName!=null and brandName!=''">
and brand_name like #{brandName}
</if>
</where>
</select>
•SQL语句会随着用户的输入或外部条件的变化而变化,我们称为 动态SQL
•MyBatis 对动态SQL有很强大的支撑:
if
choose (when, otherwise)
trim (where, set)
foreach
查询-单条件查询-动态条件
•从多个条件中选择一个
choose (when, otherwise):选择,类似于Java 中的 switch 语句
<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>
添加
1.编写接口方法: Mapper接口
参数:除了id之外的所有数据
结果:void
<insert id="add">
insert into tb_brand (brand_name, company_name, ordered, description, status)
values (#{brandName},#{companyName},#{ordered},#{description},#{status});
</insert>
3. 执行方法,测试
MyBatis事务:
openSession():默认开启事务,进行增删改操作后需要使用 sqlSession.commit(); 手动提交事务
openSession(true):可以设置为自动提交事务(关闭事务)
添加 – 主键返回
在数据添加成功后,需要获取插入数据库数据的主键
<insert id="addOrder" useGeneratedKeys="true" keyProperty="id">
insert into tb_order (payment, payment_type, status)
values (#{payment},#{paymentType},#{status});
</insert>
<insert id="addOrderItem" >
insert into tb_order_item (goods_name, goods_price, count,order_id)
values (#{goodsName},#{goodsPrice},#{count},#{orderId});
</insert>
比如:添加订单和订单项
修改 – 修改全部字段
1.编写接口方法: Mapper接口
参数:所有数据
结果:void
2.编写 SQL语句: SQL映射文件
3. 执行方法,测试
<update id="update">
update tb_brand
set brand_name = #{brandName},
company_name = #{companyName},
ordered = #{ordered},
description = #{description},
status = #{status}
where id = #{id};
</update>
修改 – 修改部分字段
3. 执行方法,测试
<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>
删除一个
<delete id="deleteById">
delete from tb_brand where id = #{id}
</delete>
批量删除
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>
MyBatis参数传递:
MyBatis接口方法中可以接受各种各样的参数,MyBatis底层对这些参数进行不同的封装处理方式
单个参数:
POJO类型:直接使用,属性名和参数占位符名称一致即可
Map集合:直接使用,键名和参数占位符名称一致
Collection:封装为map集合
map.put("arg0",collection集合)
map.put("collection",collection集合)
List:
map.put("arg0",list集合)
map.put("collection",list集合)
map.put("list",list集合)
Array
map.put("arg0",数组)
map.put("array",数组)
其他类型:直接使用
多个参数:封装为Map集合
map.put("arg0",参数值1)
map.put("param1",参数值1)
map.put("param2",参数值1)
map.put("arg0",参数值1)
如果不设置param的话会报如下错
可以通过arg0或者param1去获得参数
即 username=#{param1} /username=#{arg0}
但是不推荐这样去获取,因为阅读性不强,可以使用param注解去替换掉map集合中默认键名(arg)。
注解完成增删改查
使用注解开发会比配置文件开发更加方便
@Select(value = "select * from tb_user where id = #{id}")
public User select(int id);
查询:@Select
添加:@Insert
修改:@Update
删除:@Delete
提示:
@Select(value = "select * from tb_user where id = #{id} and username = #{username}")
public User select(@Param("id") int id, @Param("username") String username);
@Select(value = "select * from tb_user where id = #{id} and username = #{username}")
@ResultMap("userResultMap")
public User select(@Param("id") int id, @Param("username") String username);