JDBC
jdbc的demo
- 创建一个数据库
CREATE DATABASE jdbcStudy CHARACTER SET utf8 COLLATE utf8_general_ci;
USE jdbcStudy;
CREATE TABLE users(
`id` INT PRIMARY KEY,
`name` VARCHAR(40),
`password` VARCHAR(40),
`email` VARCHAR(60),
`birthday` DATE
);
INSERT INTO users(id,NAME,PASSWORD,email,birthday)
VALUES(1,'zhansan','123456','zs@qq.com','1980-12-04'),
(2,'lisi','123456','lis@qq.com','1989-02-05'),
(3,'wangwu','123456','wangwu@qq.com','1990-10-06');
- 一个demo
步骤总结:
(1) 加载驱动:Class.forName(“com.mysql.jdbc.Driver”); 这个是固定写法
(2) 连接数据库,需要用户名,密码,以及对应的url。url对应着jdbc:mysql://localhost:3306/数据库的名字?连接编码以及格式和安全连接。
- 其中这个jdbc:后面的连接是为了同一编码格式
jdbc:mysql://localhost:3306/数据库名?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true
// 统一代码的编码和数据库的编码。全部使用utf-8格式。
// 作用指定字符集的编码/解码的格式
useUnicode 默认为false 是否使用Unicode字符集,为true则是使用Unciode字符集
characterEncoding 要指定字符的编码集
mysql数据库使用的是gbk编码,而项目数据库使用的是utf8编码,这时候添加useUnicode=true&characterEncoding=UTF-8作用有两个方面
-
- 存储数据时:数据库存放项目数据会先用utf8格式将数据解码成字节码,然后再将解码后的字节码重新使用GBK编码存放到数据库中。
-
- 获取数据时:在从数据库中取数据时,数据库会先将数据库中的数据按GBK格式解码成字节码,然后再将解码后的字节码重新按utf8格式编码数据,最后再将数据返回给客户端
- 特定的格式是解码变成字节码,字节码是重新按某种格式编码成数据
- 在xml配置文件中配置数据库url时,要使用&的转义字符是==&==
- autoReconnect=true : characterEncoding=utf8可以自动被识别为utf8mb4(当然也兼容原来的utf8),autoReconnect配置建议配上
mysql -- 3306
jdbc:mysql:// 主机的地址:端口号/数据库名?参数1&参数2&参数3
// jdbc:mysql 为协议
// 如果是oralce -- 1521
jbdc:oracle:thin:@localhost:1521:sid
然后通过这个DriverManager.getConnection方法来连接数据库
// connection 代表数据库
// 数据库设置自动提交 connection.rollback(); 数据库自动提交
connection.commit(); // 事务提交
connection.setAutoCommit(); // 事务回滚
(3) 获取执行sql对象:Statement。通过createStatement来执行sql。excuteQuery来执行具体的sql
statement.executeQuery(); // 这个是查询操作返回,ResultSet
statement.execute(); // 执行任何的SQL
statement.executeupdate(); // 更新,插入,删除,(增,删,改)都是用这个,返回一个影响的行数
(4) 获取返回的结果集。消息查询出来的是一列一列的集合。
ResultSet查询的结果即:封装了所有的查询结果
// 获取指定的数据类型
resultSet.getObject(); // 在不知道列类型的情况下使用
// 如果知道列类型的情况就使用指定的类型
// 如:
resultSet.getString(); // 获取字符串等
resultSet.next(); // 移动到下一个数据,用于遍历,指针
(5) 释放连接
resultSet.close();
statement.close();
connection.close(); // 节约资源。用完关掉
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
</dependencies>
import java.sql.*;
public class jdbcDemo01 {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
// 1.加载驱动
Class.forName("com.mysql.jdbc.Driver"); // 固定写法,加载驱动
// 2. 用户的信息和url
String url = "jdbc:mysql://localhost:3306/jdbcStudy?useUnicode=true&characterEncoding=utf8&useSSL=true";
String username = "root";
String password = "123";
// 3.连接成功,数据库和对象
Connection connection = DriverManager.getConnection(url, username, password);
// 4.执行sql
Statement statement = connection.createStatement();
// 5.执行sql对象 去执行sql,可能会存在结果,查看返回的结果
String sql= "select * from users";
ResultSet resultSet = statement.executeQuery(sql);
while(resultSet.next()){
System.out.println("id="+resultSet.getObject("id"));
System.out.println("name="+resultSet.getObject("NAME"));
System.out.println("pwd="+resultSet.getObject("PASSWORD"));
System.out.println("email="+resultSet.getObject("email"));
System.out.println("birth="+resultSet.getObject("birthday"));
}
// 6.释放连接
resultSet.close();
statement.close();
connection.close();
}
}
id=1
name=zhansan
pwd=123456
email=zs@qq.com
birth=1980-12-04
id=2
name=lisi
pwd=123456
email=lis@qq.com
birth=1989-02-05
id=3
name=wangwu
pwd=123456
email=wangwu@qq.com
birth=1990-10-06
CRUD的操作
// 增的操作
Statement st = conn.createStatement(); // 执行sql
String sql = "insert into user(...)values(...)";
// 执行增sql,增删改的sql,返回值是num
int num = st.executeUpdate(sql);
if(num > 0){
sout("插入成功");
}
// 删除
Statement st = conn.createStatement();
String sql = "delete from user where id= 1"; // 删除from哪一张表的哪个id的那一行
int num = st.executeUpdate(sql);
if(num > 0){
sout("删除成功");
}
// 改,set设置也是使用executeUpdate
Statement st = conn.createStatement();
String sql = "update user set name='' where name=''"; // 删除from哪一张表的哪个id的那一行
int num = st.executeUpdate(sql);
if(num > 0){
sout("删除成功");
}
// 查询,使用executeQuery
Statement st = conn.createStatement();
String sql = "select* from user id= 1";
int num = st.executeQuery(sql);
where(rs.next()){
// rs.getObject() ,根据获取列的数据类型,分别调用rs对应的方法,映射到java对象中
}
精简代码:使用工具类
package utils;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
public class JdbcUtils {
private static String driver = null;
private static String url = null;
private static String username = null;
private static String password = null;
static{
/**
* 用Properties读取配置文件,一共有三步:
* 创建Properties实例;
* 调用load()读取文件;
* 调用getProperty()获取配置。
*/
try {
// 使用static一开始就把这些配置信息加载出来
// 把配置的信息通过加载器加载到这里来。
InputStream is = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
// Java集合库提供了一个Properties来表示一组“配置”。由于历史遗留原因,Properties内部本质上是一个Hashtable,但我们只需要用到Properties自身关于读写配置的接口。
Properties properties = new Properties();
properties.load(is);
// 获取配置
driver = properties.getProperty("driver");
url = properties.getProperty("url");
username = properties.getProperty("username");
password = properties.getProperty("password");
//1. 加载完配置之后,加载驱动
Class.forName(driver);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
// 2. 获取连接DriverManager.getConnection
// 把中间的封装成一个方法,由于这个是一个工具类,在外面直接可以调用的,所以使用static
public static Connection getConnection() throws SQLException {
// 这三个参数,没有传进来,所以提升上面这三个参数的作用域
return DriverManager.getConnection(url,username,password);
}
// 3.释放连接资源
/*resultSet.close();
statement.close();
connection.close();*/
// 方法名释放:release,释放什么东西,释放这三个,所以是Connection,Statement ,ResultSet
public static void release(Connection connection, Statement statement, ResultSet resultSet){
if(resultSet!=null){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(statement!=null){
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(connection!=null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
测试
import org.junit.Test;
import utils.JdbcUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class jdbcTest2 {
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
@Test
public void test2(){
try {
// 获取数据库连接
connection = JdbcUtils.getConnection();
// 获取sql的执行对象
statement = connection.createStatement();
// 写sql
// String sql = "delete from users where id=5";
// 查询的sql
String sql ="select * from users where id = 1";
// 执行sql
/*int i = statement.executeUpdate(sql);
if(i>0){
System.out.println("删除成功!");
}*/
resultSet = statement.executeQuery(sql);
// 会返回一个结果集,所以遍历出来
while(resultSet.next()){
String name = resultSet.getString("name");
String password = resultSet.getString("password");
String email = resultSet.getString("email");
String birthday = resultSet.getString("birthday");
System.out.println(name);
System.out.println(password);
System.out.println(email);
System.out.println(birthday);
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(connection,statement, resultSet);
}
}
}
sql注入的问题
SQL注入的问题:在拼接sql时,有一些sql由于特殊的关系字符参与字符串的拼接,会造成安全性问题。
如:密码为:a
or a
=a
; 后面是true,前面就算是错的,也可以成功
- 解决sql注入的问题:使用PreparedStatement对象来解决
String sql = "select * from users where username='"+username+"'and password='"+password+"'";
这样会产生sql注入问题
解决方法:
- 定义sql的时候:注意:sql的参数使用?作为占位符。如:select * from user where
username = ?and password = ?; - 获取获取执行sql语句的对象 PreparedStatement Connection.prepareStatement(String sql)
- 给?赋值:
方法:setXxx(参数1,参数2)
参数1: ?的位置编号 从1开始
参数2:?的值
import utils.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Date;
public class jdbcTest3 {
public static void main(String[] args) {
Connection connection = null;
PreparedStatement pst = null;
try {
// 1. 使用工具类去连接数据库
connection = JdbcUtils.getConnection();
// 2.写sql,使用?占位符来替代参数,防止sql注入
String sql = "insert into users(id,name,password,email,birthday) values(?,?,?,?,?)";
// 预编译sql,prepareStatement预编译sql,为了防止sql注入,把Statement改为了prepareStatement
pst = connection.prepareStatement(sql);
// 因为参数是用?占位符来替代,所以需要手动赋值
// 第一个参数代表的是第几个?,第二个参数是?的值
pst.setInt(1,4);
pst.setString(2,"逆天而行");
pst.setString(3,"2141423");
pst.setString(4,"123144@qq.com");
// setDate里面的源码int parameterIndex, java.sql.Date x,第二个参数是sql的Date,直接new一个sql的Date,
// 也就是说要把这个给转换成sql的date,把util类的Date,getTime获取当前时间戳
pst.setDate(5,new java.sql.Date(new Date().getTime()));
// 执行sql,executeUpdate,使用之前的方法执行,sql需要放到这个里面
// 但是使用prepareStatement不用吧sql放入executeUpdate
int i = pst.executeUpdate();
if(i>0){
System.out.println("插入成功");
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
// 别忘记关闭资源
JdbcUtils.release(connection,pst,null);
}
}
}
同理删除
// 1. 使用工具类去连接数据库
connection = JdbcUtils.getConnection();
// 2.写sql,使用?占位符来替代参数,防止sql注入
String sql = "delete from users where id=?";
// 预编译sql,prepareStatement预编译sql,为了防止sql注入,把Statement改为了prepareStatement
pst = connection.prepareStatement(sql);
pst.setInt(1,5);
// 执行sql,executeUpdate,使用之前的方法执行,sql需要放到这个里面
// 但是使用prepareStatement不用吧sql放入executeUpdate
int i = pst.executeUpdate();
if(i>0){
System.out.println("删除成功");
}
修改
connection = JdbcUtils.getConnection();
// 2.写sql,使用?占位符来替代参数,防止sql注入
String sql = "update users set name = ? where id=?;";
// 预编译sql,prepareStatement预编译sql,为了防止sql注入,把Statement改为了prepareStatement
pst = connection.prepareStatement(sql);
// 因为参数是用?占位符来替代,所以需要手动赋值
// 第一个参数代表的是第几个?,第二个参数是?的值
pst.setString(1,"子羽");
pst.setInt(2,1);
// 执行sql,executeUpdate,使用之前的方法执行,sql需要放到这个里面
// 但是使用prepareStatement不用吧sql放入executeUpdate
int i = pst.executeUpdate();
if(i>0){
System.out.println("修改成功");
}
- PreparedStatement防止sql注入的本质就是把传递进来的参数当做字符。
事务
要么都成功,要么都失败
ACID原则
-
原子性:要么全部完成,要么全部不完成
-
一致性:总数不变
-
隔离性:多个进程互不干扰
-
持久性:一旦提交不可逆,持久化到数据库
-
隔离性的问题:
-
脏读:一个事务读取到了另一个没有提交的事务
-
不可重复读:在同一个事务内,读取到了别人插入的数据,导致前后读出来的结果不一致
-
虚读:在一个事务内,读取到了别人插入的数据,导致前后读的结果不一样。
一个转账的操作
/*创建账户表*/
create table account(
id int primary key auto_increment,
name varchar(40),
money float
);
# 插入测试的数据
insert into account(name, money) values ('A',1000);
insert into account(name, money) values ('B',1000);
insert into account(name, money) values ('C',1000);
import utils.JdbcUtils;
import javax.swing.*;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class Test4 {
public static void main(String[] args) {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
// 使用工具类连接数据库
try {
connection = JdbcUtils.getConnection();
// 写sql
// 关于事务的操作的测试
// 关闭数据库的自动提交,自动会开启事务
connection.setAutoCommit(false); // false是关闭了自动提交
// A少了100
String sql1 = "update account set money = money-100 where name='A'";
// 预编译
preparedStatement = connection.prepareStatement(sql1);
// 执行sql
preparedStatement.executeUpdate();
// B+100
String sql2 = "update account set money = money +100 where name='B'";
// 编译,然后执行
PreparedStatement preparedStatement1 = connection.prepareStatement(sql2);
preparedStatement1.executeUpdate();
// 提交,没有提交的话,数据是不会被持久化的
connection.commit();
System.out.println("成功");
} catch (SQLException e) {
// 如果失败,则默认回滚,可以不写,语句不会执行
// connection.rollback();
e.printStackTrace();
}finally {
JdbcUtils.release(connection,preparedStatement,resultSet);
}
}
}