目录
JDBC入门
- 概念:Java DataBase Connectivity Java数据库连接
* JDBC本质:其实是官方(sun公司)定义的一套操作所有关系型数据库的规则,即接口。各个数据库厂商去实现这套接口,提供数据库驱动jar包。我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类。
2. 快速入门:基本操作步骤在代码部分。这只是一段了解JDBC的代码,并不完善,完善代码请看后面。
package JDBC;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
/**
* 导包:导入驱动jar包 mysql-connector-java-5.1.37-bin.jar
* 1.复制mysql-connector-java-5.1.37-bin.jar到项目的libs目录下
* 2.右键-->Add As Library
* <p>
* 1. 导入驱动jar包 mysql-connector-java-5.1.37-bin.jar
* 2. 注册驱动
* 3. 获取数据库连接对象 Connection
* 4. 定义sql
* 5. 获取执行sql语句的对象 Statement
* 6. 执行sql,接受返回结果
* 7. 处理结果
* 8. 释放资源
*/
public class jdbcConnect {
public static void main(String[] args) throws Exception {
// 1. 加载驱动
Class.forName( "com.mysql.jdbc.Driver" );
// 2. 获取数据连接对象
Connection conn = DriverManager.getConnection( "jdbc:mysql://localhost:3306/school", "root", "root" );
// 3. 定义sql
String sql = "UPDATE student SET id =101 WHERE id=102";
// 4. 获取执行sql语句的对象 Statement
Statement stat = conn.createStatement();
// 5. 执行sql
// 返回值是受影响的行数
int i = stat.executeUpdate( sql );
// 6. 处理结果
System.out.println( i );
// 6. 释放资源
stat.close();
conn.close();
}
}
====================================================
1
Process finished with exit code 0
JDBC各个类详解
DriverManager:驱动管理对象
功能:
(1)注册驱动:告诉程序该使用哪一个数据库驱动jar
static void registerDriver(Driver driver) :注册与给定的驱动程序 DriverManager 。
写代码使用: Class.forName("com.mysql.jdbc.Driver");
通过查看源码发现:在com.mysql.jdbc.Driver类中存在静态代码块
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
注意:mysql5之后的驱动jar包可以省略注册驱动的步骤。
(2) 获取数据库连接:
* 方法:static Connection getConnection(String url, String user, String password)
* 参数:
* url:指定连接的路径
* 语法:jdbc:mysql://ip地址(域名):端口号/数据库名称
* 例子:jdbc:mysql://localhost:3306/db3
* 细节:如果连接的是本机mysql服务器,并且mysql服务默认端口是3306,则url可以简写为:jdbc:mysql:///数据库名称
* user:用户名
* password:密码
Connection:数据库连接对象
功能:
1. 获取执行sql 的对象
* Statement createStatement()
* PreparedStatement prepareStatement(String sql)
2. 管理事务:
* 开启事务:setAutoCommit(boolean autoCommit) :调用该方法设置参数为false,即开启事务
* 提交事务:commit()
* 回滚事务:rollback()
Statement:执行sql的对象
功能:
1. boolean execute(String sql) :可以执行任意的sql 了解
2. int executeUpdate(String sql) :执行DML(insert、update、delete)语句、DDL(create,alter、drop)语句
* 返回值:影响的行数,可以通过这个影响的行数判断DML语句是否执行成功 返回值>0的则执行成功,反之,则失败。
3. ResultSet executeQuery(String sql) :执行DQL(select)语句 ,返回一个结果集
案例:向employee表添加一条数据。
package JDBC;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
/**
* student表 添加一条语句 insert
*/
public class JDBCDemo1 {
public static void main(String[] args) {
Statement stat = null;
Connection conn = null;
try {
// 1.加载驱动
Class.forName( "com.mysql.jdbc.Driver" );
// 2.定义sql
String sql = "insert into employee values(7,'赵虎',19,2)";
// 3.获取Connection对象
conn = DriverManager.getConnection( "jdbc:mysql://localhost:3306/school", "root", "root" );
// 4.获取sql对象
stat = conn.createStatement();
// 5.执行sql
int count = stat.executeUpdate( sql );
// 6.操作结果
if (count > 0) {
System.out.println( "插入数据成功" );
} else {
System.out.println( "插入数据失败" );
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 7.释放资源
// 防止空指针异常
if (stat != null) {
try {
stat.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
stat.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
=================================================
插入数据成功
Process finished with exit code 0
PreparedStatement:执行sql的对象
(1) SQL注入问题:在拼接sql时,有一些sql的特殊关键字参与字符串的拼接。会造成安全性问题
1. 输入用户随便,输入密码:a' or 'a' = 'a
2. sql:select * from user where username = 'fhdsjkf' and password = 'a' or 'a' = 'a'
(2)解决sql注入问题:使用PreparedStatement对象来解决
预编译的SQL:参数使用?作为占位符
步骤:
1. 导入驱动jar包 mysql-connector-java-5.1.37-bin.jar
2. 注册驱动
3. 获取数据库连接对象 Connection
4. 定义sql
* 注意:sql的参数使用?作为占位符。 如:select * from user where username = ? and password = ?;
5. 获取执行sql语句的对象 PreparedStatement Connection.prepareStatement(String sql)
6. 给?赋值:
* 方法: setXxx(参数1,参数2)
* 参数1:?的位置编号 从1 开始
* 参数2:?的值
7. 执行sql,接受返回结果,不需要传递sql语句
8. 处理结果
9. 释放资源
(3) 注意:后期都会使用PreparedStatement来完成增删改查的所有操作
1. 可以防止SQL注入
2. 效率更高
ResultSet:结果集对象,封装查询结果
功能:
* boolean next(): 游标向下移动一行,判断当前行是否是最后一行末尾(是否有数据),如果是,则返回false,如果不是则返回true
* getXxx(参数):获取数据
* Xxx:代表数据类型 如: int getInt() , String getString()
* 参数:
1. int:代表列的编号,从1开始 如: getString(1)
2. String:代表列名称。 如: getDouble("id") 表示获取employee表里id列的值
* 使用步骤:
1. 游标向下移动一行
2. 判断是否有数据
3. 获取数据
案例:定义一个方法,查询emp表的数据将其封装为对象,然后装载集合,返回。(省略了定义employee实体类的过程)
package JDBC;
import domain.employee;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
/**
* * 定义一个方法,查询emp表的数据将其封装为对象,然后装载集合,返回。
* 1. 定义Emp类
* 2. 定义方法 public List<Emp> findAll(){}
* 3. 实现方法 select * from emp;
*/
public class JDBCDemo4 {
public static void main(String[] args) {
//调用findAll方法
List<employee> list = new JDBCDemo4().findAll();
for (employee employee : list) {
System.out.println( employee );
}
}
public List<employee> findAll() {
Connection conn = null;
Statement stat = null;
ResultSet rs = null;
List<employee> list = null;
try {
// 1.加载驱动
Class.forName( "com.mysql.jdbc.Driver" );
// 2.定义sql
String sql = "select * from employee";
// 3.获取Connection对象
conn = DriverManager.getConnection( "jdbc:mysql://localhost:3306/school", "root", "root" );
// 4.获取执行sql的对象
stat = conn.createStatement();
// 5.执行sql
rs = stat.executeQuery( sql );
// 6.处理结果,遍历结果集,封装对象,装载到集合中
list = new ArrayList<employee>();
employee emp = null;
while (rs.next()) {
//创建employee对象
emp = new employee();
emp.setId( rs.getInt( "id" ) );
emp.setName( rs.getString( "name" ) );
emp.setAge( rs.getInt( "age" ) );
emp.setDep_id( rs.getInt( "dep_id" ) );
//添加到集合中
list.add( emp );
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 释放资源
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (stat != null) {
try {
stat.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
return list;
}
}
============================================================
employee{id=1, name='张三', age=20, dep_id=3}
employee{id=2, name='李四', age=21, dep_id=3}
employee{id=3, name='王五', age=20, dep_id=3}
employee{id=4, name='老王', age=20, dep_id=2}
employee{id=5, name='大王', age=22, dep_id=2}
employee{id=6, name='小王', age=18, dep_id=2}
Process finished with exit code 0
抽取JDBC工具类
目的:简化书写
分析:
1. 注册驱动抽取
2. 抽取一个方法获取连接对象
** 不传递参数,必须保证工具类的通用性能,需要创建一个配置文件,配置文件里存放数据库地址,用户名,密码,驱动
3. 抽取一个方法释放资源
配置文件:
url=jdbc:mysql://localhost:3306/school
user=root
password=root
driver=com.mysql.jdbc.Driver
JDBC工具类:
package tool;
import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
import java.sql.*;
import java.util.Properties;
/**
* JDBC工具类
*/
public class JDBCUtils {
/**
* 静态变量才能被静态代码块和静态方法访问
*/
private static String url;
private static String user;
private static String password;
private static String driver;
/**
*文件的读取,只需要读取一次就可以拿到这些值。可以使用静态代码块,随着类的加载而执行一次
* 静态代码块中的异常只能处理,不能抛出
*/
static {
//读取资源文件,获取值
try {
// 1.创建properties集合类
Properties pro = new Properties();
//获取src路径下的文件的方式--->ClassLoader 类加载器
ClassLoader classLoader = JDBCUtils.class.getClassLoader();
URL res = classLoader.getResource( "jdbc.properties" );
String path = res.getPath();
// 2.加载文件
// pro.load( new FileReader( "E:\\IdeaProject\\ShuangYuan\\day15\\jdbc.properties" ) );
pro.load( new FileReader( path ) );
// 3.获取数据,赋值
url = pro.getProperty( "url" );
user = pro.getProperty( "user" );
password = pro.getProperty( "password" );
driver=pro.getProperty( "driver" );
Class.forName( driver );
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 获取连接
*
* @return Connection连接对象
*/
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection( url,user,password );
}
/**
* 释放资源
*
* @param stat
* @param conn
*/
public static void close(ResultSet rs, Statement stat, Connection conn) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (stat != null) {
try {
stat.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
JDBC 登录案例
需求:
1. 通过键盘录入用户名和密码
2. 判断用户登录是否成功,反馈信息打印到控制台
创建User表:
CREATE TABLE USER(
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(32),
PASSWORD VARCHAR(32)
);
INSERT INTO USER VALUES(NULL,'zhangsan','123');
INSERT INTO USER VALUES(NULL,'lisi','456');
登录代码:
package JDBC;
import tool.JDBCUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Scanner;
/**
* 需求:
* 1. 通过键盘录入用户名和密码
* 2. 判断用户是否登录成功
*/
public class JDBCDemo5 {
public static void main(String[] args) {
Scanner sc=new Scanner( System.in );
System.out.println("请输入用户名:");
String username = sc.nextLine();
System.out.println("请输入密码:");
String password = sc.nextLine();
boolean login = new JDBCDemo5().login( username, password );
if(login){
System.out.println("登录成功");
}else{
System.out.println("登录失败");
}
}
/**
* 登录方法
* @param username
* @param password
* @return
*/
public boolean login(String username, String password) {
// 若用户名和密码为空,返回false
if (username == null || password == null) {
return false;
}
Connection conn = null;
Statement stat = null;
ResultSet rs = null;
try {
// 1.获取连接
conn = JDBCUtils.getConnection();
// 2.定义sql
String sql = "select * from user where username = '" + username + "' and password = '" + password + "' ";
// 3.获取执行sql的对象
stat = conn.createStatement();
// 4.执行SQL语句
rs = stat.executeQuery( sql );
return rs.next();
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 5.释放资源
JDBCUtils.close( rs, stat, conn );
}
return false;
}
}
测试结果:
请输入用户名:
zhangsan
请输入密码:
123
登录成功
Process finished with exit code 0
==========================================
请输入用户名:
zhangsan
请输入密码:
32424324
登录失败
Process finished with exit code 0
==========================================
请输入用户名:
lisi
请输入密码:
456
登录成功
Process finished with exit code 0
注意:上述代码是有问题的,如下用户名和密码在数据库中都不存在,但是却登录成功了,这是为什么呢?其实我们看下它的sql语句就可以看出来,最后的字符串拼接,or 'a' = 'a' ,or后面是一个恒等式,所以自然就成立,这就是SQL注入问题--(SQL注入,就是通过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令)。
sql:select * from user where username = 'fhdsjkf' and password = 'a' or 'a' = 'a'
请输入用户名:
dasd sa
请输入密码:
a' or 'a' = 'a
登录成功
Process finished with exit code 0
解决sql注入问题的方案:使用PreparedStatement对象来解决
预编译的SQL:参数使用 ? 作为占位符
// 1.获取连接
conn = JDBCUtils.getConnection();
// 2.定义sql
String sql = "SELECT * from user WHERE username=? and password=?";
// 3.获取执行sql的对象
ps = conn.prepareStatement( sql );
// 4.给?赋值
ps.setString(1,username);
ps.setString( 2,password );
// 5.执行查询,不需要传递sql
rs = ps.executeQuery();
JDBC控制事务
(1)事务:一个包含多个步骤的业务操作。如果这个业务操作被事务管理,则这多个步骤要么同时成功,要么同时失败。
(2 ) 操作:
1. 开启事务
2. 提交事务
3. 回滚事务
(3)使用Connection对象来管理事务
* 开启事务:setAutoCommit(boolean autoCommit) :调用该方法设置参数为false,即开启事务
* 在执行sql之前开启事务
* 提交事务:commit()
* 当所有sql都执行完提交事务
* 回滚事务:rollback()
* 在catch中回滚事务
public class JDBCDemo10 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pstmt1 = null;
PreparedStatement pstmt2 = null;
try {
//1.获取连接
conn = JDBCUtils.getConnection();
//开启事务
conn.setAutoCommit(false);
//2.定义sql
//2.1 张三 - 500
String sql1 = "update account set balance = balance - ? where id = ?";
//2.2 李四 + 500
String sql2 = "update account set balance = balance + ? where id = ?";
//3.获取执行sql对象
pstmt1 = conn.prepareStatement(sql1);
pstmt2 = conn.prepareStatement(sql2);
//4. 设置参数
pstmt1.setDouble(1,500);
pstmt1.setInt(2,1);
pstmt2.setDouble(1,500);
pstmt2.setInt(2,2);
//5.执行sql
pstmt1.executeUpdate();
// 手动制造异常
int i = 3/0;
pstmt2.executeUpdate();
//提交事务
conn.commit();
} catch (Exception e) {
//事务回滚
try {
if(conn != null) {
conn.rollback();
}
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}finally {
JDBCUtils.close(pstmt1,conn);
JDBCUtils.close(pstmt2,null);
}