什么是JDBC?
- JDBC是(java database connectivity),是sun公司采用java语言编写的链接数据库的标准规则接口。
JDBC能帮我们做什么?
- jdbc使用统一的一套java代码可以实现操作所有关系型数据库。其接口操作如下所示:
- sun公司提供统一的数据库驱动接口,各大数据库厂商根据sun公司提供的接口来编写自己的实现类,并封装成jar包,此类jar包也就成了各大厂商的数据库驱动了,当我们需要使用某种数据库时,只需要将数据库对应的jar包导入进来即可正常连接数据库。(真正执行的代码是驱动jar包中的实现类)。
JDBC 如何使用?
使用步骤:
1.导入驱动jar包
手动导入
- 复制mysql-connector-java-5.1.37-bin.jar到项目的libs目录下
- 右键–>Add As Library
maven 依赖导入
注册驱动步骤
1、 获取数据库连接对象connection
2、 定义sql
3、 获取sql 语句的对象statement
4、 执行sql,接收返回结果
5、 处理结果
7、 释放资源
java操作数据库案例
public class JdbcTest {
public static void main(String[] args) throws ClassNotFoundException {
Connection connection = null;
Statement statement = null;
try {
Class.forName("com.mysql.jdbc.Driver");
/*connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test2",
"root","root");*/
connection = DriverManager.getConnection("jdbc:mysql:///test2",
"root", "root");
statement = connection.createStatement();
String sql = "update account set balance = 1000; -- where id =1;";
int i = statement.executeUpdate(sql);
if(i<1){
System.out.println("执行失败!!!");
}else{
System.out.println(i);
System.out.println("执行成功!!!");
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
if(statement !=null){
try {
statement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(connection !=null){
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
}
jdbc中常用的对象
DriverManager 驱动管理对象
DriverManager管理对象都做了什么事情?
其实DriverManager 所做的事情主要是由以下两部分组成。
1、注册驱动
注册驱动其实是告诉系统是在使用哪一个数据库驱动jar包。
在DriverManager 中有一个静态函数:
public static void registerDriver(Driver driver)
throws SQLException
但写代码却发现直接使用Class.forName来加载驱动
Class.forName("com.mysql.jdbc.Driver");
这两者有什么关系呢?
其实秘密在于com.mysql.jdbc.Driver的源码中,源码中有静态方法,当用class.forName(“xxx”)加载文件时,com.mysql.jdbc.Driver的静态代码块自动执行,静态代码块中的注册驱动的代码就会自动执行了,其部分源码如下所示:
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
public Driver() throws SQLException {
}
static {
try {
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
}
notes:在mysql-connector-java 5.0版本之后,Class.forName(“xxx”)这个语句就可以省略了。因为在5.0后加入了配置文件,当程序加载会自动读取配置文件里面的内容,因此上述步骤可以省略。
2、获取数据库连接
获取代码方式:
static Connection getConnection(String url, String user, String password)
参数
- url : 指定连接的路径
语法:jdbc:mysql://ip地址(域名):端口号/数据库名称
例子:jdbc:mysql://localhost:3306/test1
细节:如果连接的是本机mysql服务器,并且mysql服务默认端口是3306,则url可以简写为:jdbc:mysql:///数据库名称
user: 用户名
password: 密码
Connection 数据库连接对象
功能
- 获取数据库连接:
Statement createStatement();
PrepareStatement prepareStatement(String sql);
- 管理事务
- 开启事务: setAutoCommit(boolean autoCommit):调用方法为false即为开启事务。
- 提交事务 commit();
- 回滚事务 rollback();
Statement 执行sql的对象
用于执行静态SQL语句并返回其生成的结果的对象。
boolean execute(String sql) throws SQLException
上述sql可以执行任何sql;
int executeUpdate(String sql) throws SQLException
一般情况下,该sql执行DML(insert, update, delete)语句、DDL(data definition language)(create,alter,drop)语句
返回值,表示印象的行数,可以通过这个影响的行数判断DML语句是否执行成功 返回值>0的则执行成功,反之,则失败。
ResultSet executeQuery(String sql) :执行DQL(select)语句
ResultSet: 结果集对象
本身是用于封装查询后的结果。
getXxx():获取数据。
Xxx:代表数据类型 如 int getInt(),String getString();
参数:
- int: 代表列的编号,从1开始,如: getString();
String :代表列名称。如: getDouble(“balance”)
案例一
public class Jdbc02Test {
public static void main(String[] args) {
List<Emp> all = findAll();
for (Emp emp :
all) {
System.out.println(emp);
}
}
public static List<Emp> findAll(){
Connection connection =null;
Statement statement = null;
ResultSet resultSet = null;
LinkedList<Emp> emps =null;
try {
Class.forName("com.mysql.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:mysql:///test2",
"root", "root");
statement = connection.createStatement();
String sql = "select * from account;";
resultSet = statement.executeQuery(sql);
/*while (resultSet.next()){
int anInt = resultSet.getInt(1);
String name = resultSet.getString("name");
double balance = resultSet.getDouble("balance");
System.out.println(anInt+"----->"+name+"----->"+balance);
}*/
emps = new LinkedList<>();
while (resultSet.next()){
Emp emp = new Emp();
emp.setId(resultSet.getInt(1));
emp.setName(resultSet.getString("name"));
emp.setBalance(resultSet.getDouble("balance"));
emps.add(emp);
}
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}finally {
if(resultSet != null){
try {
resultSet.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(statement != null){
try {
statement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(connection != null){
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
return emps;
}
}
查询结果:
案例二
采用list形式将查询的结果打印出来
实体类:
public class Emp {
private int id;
private String name;
private Double balance;
public Emp() {
}
public Emp(int id, String name, Double balance) {
this.id = id;
this.name = name;
this.balance = balance;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getBalance() {
return balance;
}
public void setBalance(Double balance) {
this.balance = balance;
}
@Override
public String toString() {
return "Emp{" +
"id=" + id +
", name='" + name + '\'' +
", balance=" + balance +
'}';
}
}
测试类:
public class JdbcTest {
public static void main(String[] args) throws ClassNotFoundException {
Connection connection = null;
Statement statement = null;
try {
Class.forName("com.mysql.jdbc.Driver");
/*connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test2",
"root","root");*/
connection = DriverManager.getConnection("jdbc:mysql:///test2",
"root", "root");
statement = connection.createStatement();
String sql = "update account set balance = 1000; -- where id =1;";
int i = statement.executeUpdate(sql);
if(i<1){
System.out.println("执行失败!!!");
}else{
System.out.println(i);
System.out.println("执行成功!!!");
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
if(statement !=null){
try {
statement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(connection !=null){
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
}
PreparedStatement
SQL注入问题:
- 在拼接sql时,有一些sql的特殊关键字参与字符串的拼接。会造成安全性问题,如JDBCUtils中的案例二中就发生了sql注入现象
- 输入用户随便,输入密码:a’ or ‘a’ = 'a
- 在实际数据库中所执行的sql如下所示
select
*
from
account
where
username = 'woailuo'
and
password= 'wo'
or
'ailuo' = 'ailuo';
如何解决sql注入问题
sun公司提供了PreparedStatement对象来解决sql注入的问题。
预编译的SQL
参数使用?作为占位符
抽取JDBCUtils 类
案例一
将加载资源文件和释放资源文件抽象成一个工具类,然后实现数据库操作
资源文件
url=jdbc:mysql:///test2
username=root
password=root
driver=com.mysql.jdbc.Driver
实体类
public class Emp {
private int id;
private String name;
private Double balance;
public Emp() {
}
public Emp(int id, String name, Double balance) {
this.id = id;
this.name = name;
this.balance = balance;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getBalance() {
return balance;
}
public void setBalance(Double balance) {
this.balance = balance;
}
@Override
public String toString() {
return "Emp{" +
"id=" + id +
", name='" + name + '\'' +
", balance=" + balance +
'}';
}
}
工具类
public class JDBCUtils {
private static String url;
private static String username;
private static String password;
private static String driver;
//静态代码块,加载资源文件
static {
try {
Properties properties = new Properties();
//加载文件
ClassLoader classLoader = JDBCUtils.class.getClassLoader();
URL resource = classLoader.getResource("jdbc.properties");
String path = resource.getPath();
System.out.println(path);
properties.load(new FileReader(path));
url = properties.getProperty("url");
username = properties.getProperty("username");
password = properties.getProperty("password");
driver = properties.getProperty("driver");
Class.forName(driver);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static Connection getConnection(){
Connection connection = null;
try {
connection = DriverManager.getConnection(url, username, password);
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
return connection;
}
}
public static void close(Statement statement, Connection connection){
if(statement != null){
try {
statement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(connection != null){
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
public static void close(ResultSet resultSet, Statement statement, Connection connection){
if(resultSet != null){
try {
resultSet.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(statement != null){
try {
statement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(connection != null){
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
测试类
Connection connection =null;
Statement statement = null;
ResultSet resultSet = null;
LinkedList<Emp> emps =null;
try {
connection = JDBCUtils.getConnection();
String sql = "select * from account;";
statement = connection.createStatement();
resultSet = statement.executeQuery(sql);
emps = new LinkedList<>();
while (resultSet.next()){
Emp emp = new Emp();
emp.setId(resultSet.getInt(1));
emp.setName(resultSet.getString("name"));
emp.setBalance(resultSet.getDouble("balance"));
emps.add(emp);
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JDBCUtils.close(resultSet,statement,connection);
return emps;
}
测试结果
案例二
演示代码:
工具类、资源文件都与案例一保持一致,案例二无需实体类,需要稍微修改一下数据表。
需要修改数据库。
use test2;
alter TABLE account add PASSWORD VARCHAR(20);
ALTER TABLE account CHANGE PASSWORD password VARCHAR(20);
ALTER TABLE account CHANGE NAME username VARCHAR(20);
UPDATE account SET PASSWORD = 'zhangsan' WHERE `NAME`='zhangsan';
UPDATE account SET PASSWORD = 'lisi' WHERE `NAME`='lisi';
SELECT * FROM account;
desc account;
数据库表和表结构如下所示:
测试类:
public class jdbc03Test {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入用户名:");
String username = scanner.nextLine();
System.out.println("请输入密码:");
String password = scanner.nextLine();
boolean flag = login(username, password);
if(flag){
System.out.println("登录成功!!!");
}else {
System.out.println("登录失败!!!");
}
}
public static boolean login(String username, String password){
Connection connection =null;
Statement statement = null;
ResultSet resultSet = null;
boolean loginFlag = false;
try {
connection = JDBCUtils.getConnection();
String sql = "select * from account where username = '"+username+"' and password= '"+password+"';";
System.out.println(sql);
statement = connection.createStatement();
resultSet = statement.executeQuery(sql);
if(resultSet.next()){
loginFlag = true;
System.out.println(resultSet.getString(2));
return loginFlag;
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JDBCUtils.close(resultSet,statement,connection);
}
return loginFlag;
}
}
演示sql 注入问题。
当随便输入账号:woailuo 密码:wo’ or ‘ailuo’ = 'ailuo
我们发现竟然成功的登录了。
可见sql注入问题还是比较严重。
我们已经知晓PreparedStatement可以解决sql注入问题,下面的案例三将展示如何使用PreparedStatement对象。
案例三
资源类,工具类保持与案例二一致,改动statement对象为preparestatement
public class JDBC04Test {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入用户名:");
String username = scanner.nextLine();
System.out.println("请输入密码:");
String password = scanner.nextLine();
boolean flag = login(username, password);
if(flag){
System.out.println("登录成功!!!");
}else {
System.out.println("登录失败!!!");
}
}
public static boolean login(String username,String password){
if(username == null || password==null){
return false;
}
Connection connection =null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
boolean loginFlag = false;
try {
connection = JDBCUtils.getConnection();
String sql = "select * from account where username = ? and password= ?;";
System.out.println(sql);
preparedStatement = connection.prepareStatement(sql);
//需要给问好赋值
preparedStatement.setString(1,username);
preparedStatement.setString(2,password);
resultSet = preparedStatement.executeQuery();
if(resultSet.next()){
loginFlag = true;
System.out.println(resultSet.getString(2));
return loginFlag;
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JDBCUtils.close(resultSet,preparedStatement,connection);
}
return loginFlag;
}
}
sql注入失败,此问题得到解决。
jdbc事务控制
jdbc 事务控制过程
事务控制可以采用Connection对象。
开启事务:
- setAutoCommit(boolean autoCommit) :调用该方法设置参数为false,即开启事务在执行sql之前开启事务:
提交事务:
- commit(); 同一事务中所有sql执行完成后则提交事务。
回滚事务:
- rollback(); 在catch中回滚事务
也可以来实现事务管理
案例一
本案例中工具类,资源类与抽取JDBCUtils 类保持一致
测试类
public class JDBC05Test {
public static void main(String[] args) {
System.out.println("----------交易开始前----------");
List<Emp> all = findAll();
for (Emp emp :
all) {
System.out.println(emp);
}
transaction("zhangsan","lisi",500);
System.out.println("------交易完成后-------");
all=findAll();
for (Emp emp :
all) {
System.out.println(emp);
}
}
public static boolean transaction(String user1, String user2, double val ){
Connection connection =null;
PreparedStatement preparedStatement1 = null, preparedStatement2=null;
ResultSet resultSet = null;
try {
connection = JDBCUtils.getConnection();
connection.setAutoCommit(false);
String sql1 = " update account set balance = balance - ? where username = ?";
String sql2 = " update account set balance = balance + ? where username = ?";
preparedStatement1 = connection.prepareStatement(sql1);
preparedStatement2 = connection.prepareStatement(sql2);
preparedStatement1.setDouble(1,val);
preparedStatement1.setString(2,user1);
preparedStatement2.setDouble(1,val);
preparedStatement2.setString(2,user2);
int i = preparedStatement1.executeUpdate();
// int i2= 4/0;
int i1 = preparedStatement2.executeUpdate();
connection.commit();
} catch (Exception e) {
try {
if(connection !=null){
connection.rollback();
}
} catch (SQLException sqlException) {
sqlException.printStackTrace();
}
e.printStackTrace();
return false;
}finally {
JDBCUtils.close(resultSet,preparedStatement1,connection);
JDBCUtils.close(resultSet,preparedStatement2,connection);
}
return true;
}
public static List<Emp> findAll(){
Connection connection =null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
LinkedList<Emp> emps =null;
try {
connection = JDBCUtils.getConnection();
String sql = "select * from account;";
preparedStatement = connection.prepareStatement(sql);
resultSet = preparedStatement.executeQuery();
emps = new LinkedList<>();
while (resultSet.next()){
Emp emp = new Emp();
emp.setId(resultSet.getInt(1));
emp.setUsername(resultSet.getString("username"));
emp.setBalance(resultSet.getDouble("balance"));
emp.setPassword("password");
emps.add(emp);
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JDBCUtils.close(resultSet,preparedStatement,connection);
return emps;
}
}
}
当事务中没有异常时刻即整个事务正常运行
当事务中存在异常则直接回滚事务。
public class JDBC05Test {
public static void main(String[] args) {
System.out.println("----------交易开始前----------");
List<Emp> all = findAll();
for (Emp emp :
all) {
System.out.println(emp);
}
transaction("zhangsan","lisi",500);
System.out.println("------交易完成后-------");
all=findAll();
for (Emp emp :
all) {
System.out.println(emp);
}
}
public static boolean transaction(String user1, String user2, double val ){
Connection connection =null;
PreparedStatement preparedStatement1 = null, preparedStatement2=null;
ResultSet resultSet = null;
try {
connection = JDBCUtils.getConnection();
connection.setAutoCommit(false);
String sql1 = " update account set balance = balance - ? where username = ?";
String sql2 = " update account set balance = balance + ? where username = ?";
preparedStatement1 = connection.prepareStatement(sql1);
preparedStatement2 = connection.prepareStatement(sql2);
preparedStatement1.setDouble(1,val);
preparedStatement1.setString(2,user1);
preparedStatement2.setDouble(1,val);
preparedStatement2.setString(2,user2);
int i = preparedStatement1.executeUpdate();
int i2= 4/0;
int i1 = preparedStatement2.executeUpdate();
connection.commit();
} catch (Exception e) {
try {
if(connection !=null){
connection.rollback();
}
} catch (SQLException sqlException) {
sqlException.printStackTrace();
}
e.printStackTrace();
return false;
}finally {
JDBCUtils.close(resultSet,preparedStatement1,connection);
JDBCUtils.close(resultSet,preparedStatement2,connection);
}
return true;
}
public static List<Emp> findAll(){
Connection connection =null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
LinkedList<Emp> emps =null;
try {
connection = JDBCUtils.getConnection();
String sql = "select * from account;";
preparedStatement = connection.prepareStatement(sql);
resultSet = preparedStatement.executeQuery();
emps = new LinkedList<>();
while (resultSet.next()){
Emp emp = new Emp();
emp.setId(resultSet.getInt(1));
emp.setUsername(resultSet.getString("username"));
emp.setBalance(resultSet.getDouble("balance"));
emp.setPassword("password");
emps.add(emp);
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JDBCUtils.close(resultSet,preparedStatement,connection);
return emps;
}
}
}
至此jdbc的基本知识就复习到这里。