JDBC(Java DataBase Connective)
Java连接数据库的规范 - 接口
Connection - 连接
// 1.导入jar包
/* 2.加载驱动
可能产生异常: ClassNotFoundException
原因: 1.没有导入jar包
2.类名写错
*/
Class.forName("com.mysql.cj.jdbc.Driver");
// 3.获得连接对象 DriverManager
/* DriverManager 提供了一个静态方法 getConnection
参数1: 连接数据库的url地址
jdbc:mysql://ip:port/dataBase?serverTimezone=GMT
参数2: 连接数据库的账号
参数3: 连接数据库的密码
可能产生异常: SQLException
1.dataBase 指定错误
2.账号/密码错误
3.端口错误 -> Connection refused
4.ip错误 -> Network is unreachable
5.jdbc:mysql 错误 -> No suitable driver
结论: 连接数据库url需要匹配合适的驱动类
*/
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db_day01?serverTimezone=GMT", "root", "aaa");
// 4.获得操作数据库的Statement对象 - 执行sql语句
Statement stmt = conn.createStatement();
// 5.执行sql语句 DDL DML DQL
String sql = "create table u (id int primary key, name varchar(20))";
stmt.execute(sql);
// 6.释放资源
stmt.close();
conn.close();
执行sql语句的方法有三个
- boolean execute(String sql)
DDL DML - 返回 false
DQL - 返回true
因为DML 和 DQL有专门的执行方法, 所以该方法通常用来执行DDL
返回值:有没有结果
Statement stmt = conn.createStatement();
// 执行DDL
String sql = "create table u1 (id int primary key, name varchar(20))";
boolean b = stmt.execute(sql); // false
Statement stmt = conn.createStatement();
// 执行DML
String sql = "insert into u values(1, 'lucy')";
boolean b = stmt.execute(sql); // false
Statement stmt = conn.createStatement();
// 执行DQL
String sql = "select * from u ";
boolean b = stmt.execute(sql); // true
- int executeUpdate(String sql)
只能执行 DML
返回值:影响了几行
Statement stmt = conn.createStatement();
// 执行DML
String sql = "insert into u values(2, 'tom')";
int i = stmt.executeUpdate(sql); // 1
- ResultSet executeQuery(String sql)
只能执行 DQL
返回值:查询出来的结果集
Statement stmt = conn.createStatement();
// 执行DQL
String sql = "select id i, name from u ";
ResultSet rs = stmt.executeQuery(sql); // 返回结果集
while (rs.next()) {
// ResultSet获得的数据 就是当前指针位置这一行的数据
// 获得列名为 i 的值 下标从1开始
int id1 = rs.getInt("i");
String name1 = rs.getString("name");
System.out.println(id1 + " -- " + name1);
}
PreparedStatement
-
PreparedStatement extends Statement
-
预编译(sql就是一个半成品 - [? 占位符])
例: select * from user where name = ? and password = ?
执行sql语句:给占位符传参 -
PreparedStatement 可以有效防止SQL注入 (重点)
SQL注入:通过字符串的拼接, 将整个SQL语句的语义结构改变了, 从而达到一定目的(不安全的语法, 一定要避免)
例:
SQL语句:
select * from user
where name = ‘111’ and password = ‘a’
or ‘1’=‘1’ -
当多次执行相同的/相似的SQL, 只需要预编译一次
而Statement需要编译多次, PreparedStatement效率更高 -
批量执行(了解)
String sql = "insert into user values(null,?,?,?,?)";
// 预编译SQL, 一次
PreparedStatement stmt = conn.prepareStatement(sql);
for (int i = 0; i < 10; i++) {
stmt.setString(1, "ww"+i);
stmt.setInt(2, 28);
stmt.setString(3, "2020-08-01");
stmt.setString(4, "123");
// 不马上执行SQL
// 添加到SQL执行的队列中
stmt.addBatch();
}
// 批量执行SQL队列中所有的语句
stmt.executeBatch();
// 清空之前的执行队列
stmt.clearBatch();
JDBC事务
绑定在连接对象上的,都是默认自动提交
- 设置事务手动提交
conn.setAutoCommit(false) - 代码正常结束,提交事务
conn.commit(); - 代码出现异常,回滚事务(一般放在异常中)
conn.rollback() - 了解
保存事务结点
SavePoint a = conn.setSavePoint()
回滚到指定的结点
conn.rollback(a)
注:mysql数据库 插入时间,可以是一个字符串
一般将创建连接和关闭连接 封装在一个工具类中 、把信息存在配置文件中(重点)
/*
工具类的封装
*/
public class JDBCUtils {
private static String driver ;
private static String url ;
private static String user ;
private static String password ;
static {
// 读取属性集文件
Properties pro = new Properties();
try {
pro.load(JDBCUtils.class.getResourceAsStream("db.properties"));
driver = pro.getProperty("driver");
url = pro.getProperty("url");
user = pro.getProperty("user");
password = pro.getProperty("password");
Class.forName(driver);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static Connection getConnection() {
Connection conn = null;
try {
conn = DriverManager.getConnection(url, user, password);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return conn;
}
// 关闭连接
public static void close(ResultSet rs, Statement stmt, Connection conn) {
try {
// 避免空指针
if (rs != null) {
rs.close();
}
if (stmt != null) {
stmt.close();
}
if (conn != null) {
conn.close();
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
// 关闭连接(重载)
public static void close(Statement stmt, Connection conn) {
try {
// 避免空指针
if (stmt != null) {
stmt.close();
}
if (conn != null) {
conn.close();
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
简单登录案例:
public class LoginDemo {
public static void main(String[] args) {
// 1.控制台输入账号密码
Scanner console = new Scanner(System.in);
System.out.println("请输入账号:");
String username = console.nextLine();
System.out.println("请输入密码:");
String password = console.nextLine();
// 2.调用登录方法
User user = login(username, password);
if (user == null) {
System.out.println("用户名/密码错误!");
} else {
System.out.println(user.getName() + "欢迎你!");
}
}
public static User login(String username, String password) {
// 2.封装一个sql语句
String sql = "select * from user where name = ? and password = ?";
// 3.连接数据库, 查询 executeQuery
Connection conn = JDBCUtils.getConnection();
try {
// 获得预编译的执行sql的对象
PreparedStatement pstmt = conn.prepareStatement(sql);
// 预编译执行对象 执行sql语句之前传入参数
pstmt.setString(1, username);
pstmt.setString(2, password);
ResultSet rs = pstmt.executeQuery();
// 4.ResultSet
// 有没有登录成功 next() 是true - 成功
if (rs.next()) {
User user = new User();
user.setId(rs.getInt("id"));
user.setName(rs.getString("name"));
user.setAge(rs.getInt("age"));
user.setBirthday(rs.getDate("birthday"));
user.setPassword(rs.getString("password"));
return user;
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
// 抛出异常
return null;
}
}