JDBC
概念:
JDBC(java data base connectivity)Java数据库连接,是一种用于执行sql语句的java的API
它是由使用java语言编写的类和接口组成。
本质上是sun公司定义的一套操作所有关系型数据库的规则,将来每个数据库厂商去写实现类实现接口,提供数据库jar包,程序员可以使用提供的jar包进行编程
JDBC快速实现
package com.company;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class JDBC_Demo {
public static void main(String[] args) {
//1.导入jar包:复制到src目录,右击选择 ---add as library
//2。记载(注册)驱动
//将来要抛出直接选中要抛出的全部代码块,ctrl+alt+t选择try+catch+finally
Connection conn = null;
Statement stmt = null;
try {
Class.forName("com.mysql.jdbc.Driver");//抛出异常
//3,通过注册的驱动,获取数据库连接 Connection 是java.util下的
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/zte",
"root","123456");
//4.定义sql语句
String sql="update emp set sal=5000 where ename='张三'";
String sql2="insert into emp values('10007','jack','clerk','2021-01-01','6000')";
String sql3="delete from emp where empno='10007'";
//5.获取到数据库连接
stmt = conn.createStatement();
//6.通过stmt对象,调用方法执行sql
//如果执行成功,那么就会返回一个值,表示执行的行数值
int i = stmt.executeUpdate(sql);
int m= stmt.executeUpdate(sql2);
int n= stmt.executeUpdate(sql3);
//7.处理结果
System.out.println(i >0 ? "i执行成功":"i执行失败");
System.out.println(m >0 ? "m执行成功":"m执行失败");
System.out.println(n >0 ? "m执行成功":"m执行失败");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
//8.关闭资源
try {
stmt.close();
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
数据库连接:mysql&oracle版本
jdbc:mysql://localhost:3306/数据库名?serverTimezone=GMT%2B8&useSSL=false&useUnicode=true&characterEncoding=utf-8
oracle.jdbc.OracleDriver
jdbc:oracle:thin:@//192.168.100.160:1521/helowin
连接oracle数据库
package com.company;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class JDBC_Oracle {
public static void main(String[] args) {
//1.导入jar包:复制到src目录,右击选择 ---add as library
//2。记载(注册)驱动
Connection conn = null;
Statement stmt=null;
try {
Class.forName("oracle.jdbc.OracleDriver");//抛出异常
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//3,通过注册的驱动,获取数据库连接 Connection 是java.util下的
try {
conn = DriverManager.getConnection("jdbc:oracle:thin:@//192.168.60.100:1521/orcl",
"scott","123456");
} catch (SQLException e) {
e.printStackTrace();
}
//4.定义sql语句
String sql="update emp set sal=5000 where empno=7499";
//5.获取到数据库连接
try {
stmt = conn.createStatement();
} catch (SQLException e) {
e.printStackTrace();
}
//6.通过stmt对象,调用方法执行sql
//如果执行成功,那么就会返回一个值,表示执行的行数值
int i= 0;
try {
i = stmt.executeUpdate(sql);
} catch (SQLException e) {
e.printStackTrace();
}
//7.处理结果
System.out.println(i >0 ? "i执行成功":"i执行失败");
//8.关闭资源
try {
stmt.close();
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
JDBC实现查询(mysql版本)
创建的同名实体类:
package com.company;
import java.util.Date;
/*实体类:把数据查询到的数据存放到实体类对象里,返回给前端
* 保证实体类的属性名称和类型与数据库表一致*/
public class Emp {
private int empno;
private String ename;
private String job;
private Date hiredate;
private double sal;
//构造方法:全参加无参
public Emp() {
}
public Emp(int empno, String ename, String job, Date hiredate, double sal) {
this.empno = empno;
this.ename = ename;
this.job = job;
this.hiredate = hiredate;
this.sal = sal;
}
@Override
public String toString() {
return "Emp{" +
"empno=" + empno +
", ename='" + ename + '\'' +
", job='" + job + '\'' +
", hiredate=" + hiredate +
", sal=" + sal +
'}';
}
public int getEmpno() {
return empno;
}
public void setEmpno(int empno) {
this.empno = empno;
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
public Date getHiredate() {
return hiredate;
}
public void setHiredate(Date hiredate) {
this.hiredate = hiredate;
}
public double getSal() {
return sal;
}
public void setSal(double sal) {
this.sal = sal;
}
}
创建与表名同名实体类,通过创建集合来存放赋值后的实体类
package com.company;
import java.sql.*;
public class JDBCDemo02 {
public static void main(String[] args) {
Connection connection= null;
Statement statement = null;
ResultSet resultSet = null;
try {
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/zte",
"root","123456");
//conn获取statement
statement = connection.createStatement();
//执行查询
//定义sql
String sql="select * from emp";
//获取结果集
resultSet = statement.executeQuery(sql);
//获取rs中的数据
while (resultSet.next()){
//表示获取数据库中第一列,数据类型是int类型的字段 括号里可以写字段所在行数或者直接输入字段名称
int id = resultSet.getInt(1);
String name = resultSet.getString(2);
String job = resultSet.getString(3);
String hiredate = resultSet.getString("hiredate");
String sal = resultSet.getString("sal");
System.out.println(id+" "+name+" "+job+" "+hiredate+" "+sal);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (resultSet !=null){
resultSet.close();
}
if (statement !=null){
statement.close();
}
if (connection !=null){
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
}
}
}
}
JDBC中使用的对象总结:
DriverManager :驱动管理对象
功能:
1,注册驱动,告诉程序使用哪个数据库驱动jar包,mysql5版本之后,可以省略这一步2,获取数据库连接Connection:
static Connection getConnection(String url, String user, String password)尝试建立与给定数据库URL的连接。
参数:url表示连接数据库的地址
mysql地址: jdbc:mysql://主机ip:3306/数据库名
oracle地址: jdbc:oracle:thin:@//主机ip:1521/数据库名user表示连接数据库的用户名
password表示连接数据库的密码
Connection:数据库连接对象
功能︰
1,获取执行sql语句的对象
Statement createStatement()
创建一个Statement对象,用于将SQL语句发送到数据库。PreparedStatement prepareStatement(String sql)
创建一个 PreparedStatement对象,用于将参数化的SQL语句发送到数据库。2,管理事务
void setAutoCommit(boolean autoCommit)将此连接的自动提交模式设置为给定状态。
void commit()使自上次提交/回滚以来所做的所有更改
void rollback()撤消在当前事务中所做的所有更改
Statement :执行sql对象
功能:
1,执行sql语句
ResultSet executeQuery(String sql)执行给定的SQL语句,该语句返回单个ResultSet对象。
int executeUpdate(String sql)执行给定的SQL语句,这可能是INSERT,UPDATE,或DELETE语句,或者不返回任何内容,如SQL DDL语句的SQL语句。
ResultSet :查询返回的结果集对象,封装查询结果的
ResultSet对象保持一个光标指向其当前的数据行。
最初,光标位于第一行之前。next方法将光标移动到下一行,并且由于在ResultSet对象中没有更多行时返回false,因此可以在while循环中使用循环来遍历结果集。
常用方法:
1, boolean next()将光标从当前位置向前移动一行。2,getXxx(参数)方法︰获取结果集中具体值的方法
方法名Xxx:获取到的数据类型是什么类型,这里的Xxx就用什么类型
比如,字段是int类型的,那么就用getlnt(),字段是字符串类型,就用getString()字段是小数类型,就用getDouble()
参数︰参数可以传入两个类型,一个int,一个String
int代表传入的是获取第几列的数据
String代表传入获取指定的字段名的数据,如果字段名是sname,类型String方法的写法: getString("sname")
PreparedStatement :处理预编译的SQL
SQL注入问题∶
使用PrepareStatement解决sql注入问题
编写SQL语句的时候,使用字符串拼接的方式,拼接数据,会产生的一些问题
为了避免这个问题,JDBC中提供了PreparedStatement对象,可以通过处理预编译SQL的方式,完成SQL的查询
用法︰
1,定义sql的时候,将需要传入的参数,使用?代替,
比如SELECT* FROM USER WHERE NAME = ? AND pwd = ?
2,conn对象调用方法,传入sql语句,进行预编译处理,并返回pstmt对象3,通过pstmt对象,完成?(占位符)的赋值,调用setXxx(参数)方法
set方法的名称类型和你要赋值的实际字段类型─致
参数:第一个参数表示给第几个?赋值,第二个参数表示具体的值4,通过pstmt对象,调用方法,执行sql
executeQuery()执行查询executeUpdate()执行增删改
使用java+jdbc完成简单的用户登录功能
package com.company;
import java.sql.*;
import java.util.ArrayList;
import java.util.Scanner;
public class JDBCLoginDemo {
public static void main(String[] args) {
//模拟用户登录功能
Scanner scanner = new Scanner(System.in);
System.out.println("请输入用户名");
String username = scanner.nextLine();
System.out.println("请输入密码");
String userpassword=scanner.nextLine();
Connection connection= null;
Statement statement = null;
ResultSet resultSet = null;
try {
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/zte",
"root","123456");
//conn获取statement
statement = connection.createStatement();
//执行查询
//定义sql
String selectuser = "select * from user where name ='"+ username+"' and pwd = '"+userpassword+"'";
//获取结果集
resultSet = statement.executeQuery(selectuser);
if (resultSet.next()){
System.out.println("用户存在,登录成功");
}else {
System.out.println("用户不存在,登录失败");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (resultSet !=null){
resultSet.close();
}
if (statement !=null){
statement.close();
}
if (connection !=null){
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
statement只能支持字符串拼接,因此可能会出现SQL注入问题
为此,提供了preparestatement来解决问题
使用PrePareStatement优化版
package com.company;
import java.sql.*;
import java.util.Scanner;
public class PrepareSteateDemo {
public static void main(String[] args) {
//模拟用户登录功能
Scanner scanner = new Scanner(System.in);
System.out.println("请输入用户名");
String username = scanner.nextLine();
System.out.println("请输入密码");
String userpassword=scanner.nextLine();
Connection connection= null;
PreparedStatement pstmt=null;
ResultSet resultSet = null;
try {
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/zte",
"root","123456");
//执行查询
//定义sql
String selectuser = "select * from user where name = ? and pwd = ? ";
pstmt=connection.prepareStatement(selectuser);
//conn获取pstmt对象
pstmt.setString(1,username);
pstmt.setString(2,userpassword);
//获取结果集
resultSet = pstmt.executeQuery();
if (resultSet.next()){
System.out.println("用户存在,登录成功");
}else {
System.out.println("用户不存在,登录失败");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (resultSet !=null){
resultSet.close();
}
if (pstmt !=null){
pstmt.close();
}
if (connection !=null){
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
将来在日常开发中都不再使用Statement而使用PrepareStatement
executeQuery()方法:执行查询,括号里放定义的sql语句
executeUpdate()方法:执行增删改
JDBC事务
通过转账案例模拟事务的处理流程
一次转账中,包含两次修改操作,如果这两次操作之中发生了异常,应该对事务进行回滚,否则数据的一致性不能得到保证。
package com.company;
import java.sql.*;
public class JDBCComment {
public static void main(String[] args) {
try {
Connection coon= JDBCUtil.getConnection();
coon.setAutoCommit(false);//手动打开事务
String sql1="update tb_account set money =money-? where name=?";
PreparedStatement srpst =coon.prepareStatement(sql1);
srpst.setInt(1,200);
srpst.setString(2,"zhangsan");
int i=srpst.executeUpdate();
int m=1;
int a=3/m;//模拟一个异常
//操作事务2
String sql2="update tb_account set money =money+? where name=?";
PreparedStatement srpst2 =coon.prepareStatement(sql2);
srpst2.setInt(1,200);
srpst2.setString(2,"lisi");
int j=srpst2.executeUpdate();
//提交事务
coon.setAutoCommit(true);
} catch (SQLException e) {
e.printStackTrace();
} finally {
}
}
}
编写JDBC工具类
工具类主要是为了简化书写,将共同的内容抽取到方法中使用需要抽取哪些?
1,注册驱动
2,获取数据库连接方法
解决问题:不想每次调用都去传递参数,还得保证工具类的通用性3,关闭资源方法
JDBCUtil工具类
package com.company;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
public class JDBCUtil {
//想办法可以让变量值动态的进行获取,可以根据用户的需求自定义传入url,user,password
//而且传入之后,让数据能获取,并且加载
private static String url;
private static String username;
private static String password;
private static String driver;
static{
try {
InputStream is=JDBCUtil.class.getClassLoader().getResourceAsStream("jdbc.properties");
Properties properties = new Properties();
properties.load(is);
//获取到配置文件中的数据
url =properties.getProperty("url");
username = properties.getProperty("username");
password = properties.getProperty("password");
driver = properties.getProperty("driver");
Class.forName(driver);
} catch (IOException|ClassNotFoundException e) {
e.printStackTrace();
}
}
//获取连接方法
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url,username,password);
}
//获取pstmt对象的方法封装
public static PreparedStatement getPstmt(String sql,Connection conn) throws SQLException {
return conn.prepareStatement(sql);
}
//关闭方法
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 e) {
e.printStackTrace();
} finally {
}
}
}
配置文件
//jdbc.properties配置文件
url=jdbc:mysql://localhost:3306/zte?serverTimezone=GMT%2B8&useSSL=false&useUnicode=true&characterEncoding=utf-8
username=root
password=123456
driver=com.mysql.jdbc.Driver
数据库连接池
为什么需要数据库连接池?
因为频繁的获取Connection对象,需要消耗很多的资源,所以可以利用池子技术,在连接池中放好创建好的Connection对象,下次使用,直接从池子中获取,用完之后调用close()方法,将对象再还到池子里。
package com.company.JDBCpool;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.LinkedList;
import java.util.Properties;
import java.util.logging.Logger;
/*数据库连接池需要实现一个官方提供的接口:DataSorce*/
public class JDBCPool implements DataSource {
//创建连接池
private static LinkedList<Connection> list= new LinkedList<>();
//希望类在加载的时候就在连接池中放满Connection对象(设置静态代码块)
static{
try {
InputStream is= JDBCPool.class.getClassLoader().getResourceAsStream("jdbc.properties");
//创建properties,并加载输入流
Properties properties=new Properties();
properties.load(is);
String url= properties.getProperty("url");
String username=properties.getProperty("username");
String password =properties.getProperty("password");
String driver =properties.getProperty("driver");
//注册驱动
Class.forName(driver);
for (int i = 0; i < 10; i++) {
Connection conn = DriverManager.getConnection(url,username,password);
list.add(conn);
}
} catch (IOException | ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
}
@Override
public Connection getConnection() throws SQLException {
System.out.println("当前的池子中,还有:" + list.size() +"个conn对象");
Connection conn = list.removeFirst();
System.out.println("conn对象被取走一个,当前的池子中,还有:"+list.size() +"个conn对象");
//让Proxy动态代理connection对象,判断connection将来执行的方法
return (Connection) Proxy.newProxyInstance(JDBCPool.class.getClassLoader(),
conn.getClass().getInterfaces(),
new InvocationHandler() {
//将来当connection执行方法的时候,都会经过这个invoke方法
// 只需要判断使用的是否是close方法,如果close,就做单独处理
// 如果是其他方法,正常执行
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("close")){
//将conn对象还到池子里
list.add(conn);
System.out.println("池子中归还了一个conn对象,现在大小是:"+ list.size());
} else {
//其他情况继续执行方法
method.invoke(conn, args);
}
return null;
}
});
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
return null;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
@Override
public PrintWriter getLogWriter() throws SQLException {
return null;
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
}
@Override
public int getLoginTimeout() throws SQLException {
return 0;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
}
}
数据库连接池测试类
package com.company.JDBCpool;
import java.sql.Connection;
import java.sql.SQLException;
public class JDBCTest {
public static void main(String[] args) throws SQLException {
JDBCPool Pool = new JDBCPool();
Pool.getConnection();//剩9
Pool.getConnection();//剩8
Pool.getConnection();//剩7
//这个conn获取后,就归还
Connection conn = Pool.getConnection();//剩6
conn.close();//剩7
Pool.getConnection();//剩6
}
}
运行结果: