1 JDBC基本概念
JDBC,Java Database Connectivity,Java数据库连接,Java语言操作数据库。JDBC的本质是SUN公司定义的一套操作所有关系型数据库的规则,即接口,各数据库厂商实现这套接口,提供数据库驱动jar包,用户可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类。
2 JDBC的快速入门
使用JDBC的步骤:
- 1)导入驱动jar包:mysql-connector-java-5.1.37-bin.jar; -- 复制jar包到项目的libs目录下; -- 右键,Add As Library;
- 2)编写代码,注册驱动;
- 3)获取数据库的连接对象;
- 4)定义sql语句;
- 5)获取执行sql语句的对象statement;
- 6)执行sql,接收返回结果;
- 7)处理结果;
- 8)释放资源,否则可能会造成内存的泄露问题。
快速入门代码如下:
代码语言:javascript复制public class JdbcDemo1 {
public static void main(String[] args) throws Exception {
//1)导入驱动jar包
//2)注册驱动
Class.forName("com.mysql.jdbc.Driver");
//3)获取数据库连接对象
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/db1","root","root");
//4)定义sql语句
String sql = "update account set balance = 500 where id = 2";
//5)获取执行sql的对象Statement
Statement statement = connection.createStatement();
//6)执行sql
int count = statement.executeUpdate(sql);
//7)处理结果
System.out.println(count);
//8)释放资源
statement.close();
connection.close();
}
}
3 JDBC各对象详解
3.1 DriverManager驱动管理对象
1)注册驱动:告诉程序该使用哪一个数据库驱动jar
static void registerDriver(Driver driver),注册与给定的驱动程序DriverManager,但是写代码时使用的是Class.forName("com.mysql.jdbc.Driver"),为什么?查看源码发现,在com.mysql.jdbc.Driver类中存在静态代码块:
【注意】:实际上mysql5以后的驱动jar包可以省略注册驱动的步骤,若没有写,程序会读取下面的文件实现自动注册,但是,实际开发中还是写上,避免有误区;
2)获取数据库连接
方法:public static java.sql.Connection getConnection(String url, String user, String password);
参数:url:指定连接的路径,写法:jdbc:mysql://ip地址(域名):端口号/数据库名称;
user:用户名
password:密码
【注意】:若连接是本机的mysql服务器,且mysql默认端口是3306,则url可以简写为jdbc:mysql:///数据库名称;
3.2 Connection数据库连接对象
1)获取执行sql的对象
- Statement createStatement()
- PreparedStatement prepareStatement(String sql)
2)管理事务
- 开启事务:setAutoCommit(boolean autoCommit):设置参数为false,即开启事务;
- 提交事务:commit();
- 回滚事务:rollback();
3.3 Statement执行SQL的对象
1)执行sql
- boolean execute(String sql):执行给定的sql语句,返回可能的结果(了解即可,不常用)。
- int executeUpdate(String sql):执行DML(insert、update、delete)语句、DDL(create、alter、drop)语句。 返回值:影响的行数,可以通过这个结果判断DML语句是否执行成功,返回值>0则成功,反之则失败。
- ResultSet executeQuery(String sql):执行DQL(select)语句。
【举例】:在account表中添加一条记录、修改记录、删除一条记录,要考虑异常处理及资源释放的规范性。
代码语言:javascript复制public class JdbcDemo2 {
public static void main(String[] args) {
Connection connection = null;
Statement statement = null;
try {
//1、注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2、定义sql
String sql = "insert into account values(null,'wangsan',2000)";
//3、获取Connection对象
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/db1", "root", "root");
//4、获取执行sql对象
statement = connection.createStatement();
//5、执行sql
int cnt = statement.executeUpdate(sql);
//6、处理结果
System.out.println(cnt);
if(cnt>0){
System.out.println("添加成功");
}else{
System.out.println("添加失败");
}
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}finally {
//7、释放资源
//要避免空指针异常
if(statement!=null){
try {
statement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(connection!=null){
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
}
以上程序为添加一条记录,修改、删除记录与其相似,只需要改下sql语句:
- 修改:String sql = "update account set balance = 1500 where id =3";
- 删除:String sql = "delete from account where id =3";
3.4 ResultSet结果集对象
Statement的executeQuery方法,返回的就是ResultSet结果集对象,用户需要做的就是把结果从ResultSet中取出来,涉及到两类方法:
- next()方法:游标向下移动一行(游标默认是在表头处),判断当前行是否是最后一行之后(是否有数据),若是,则返回false,否则返回true;
- getXxx(参数)方法:获取一列的数据; Xxx代表数据类型,如String getString(); 参数:Int,代表列的编号,从1 开始,如getString(1); String,代表列的名称,如getDouble("balance");
3.4.1 ResultSet的基本使用
代码语言:javascript复制public class JdbcDemo3 {
public static void main(String[] args) {
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
try {
//1、注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2、定义sql
String sql = "select * from account";
//3、获取Connection对象
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/db1", "root", "root");
//4、获取执行sql对象
statement = connection.createStatement();
//5、执行sql
resultSet = statement.executeQuery(sql);
//6、处理结果
//6.1 先让游标向下移动一行
resultSet.next();
int id = resultSet.getInt(1);
String name = resultSet.getString("name");
double balance = resultSet.getDouble(3);
System.out.println(id "--" "name" "--" balance);
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}finally {
//7、释放资源
//要避免空指针异常
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();
}
}
}
}
}
以上只是ResultSet的基本使用,前提是我们知道表中的有几条数据,但实际查询中我们不能确定查询到的结果是多少条,有存在游标超限等异常可能。
【注意使用步骤】:
- 1)游标向下移动一行;
- 2)判断是否有数据(仍使用next()方法);
- 3)获取数据;
所以,将以上代码中,处理结果的语句改进如下:
代码语言:javascript复制 //6、处理结果
//6.1 先让游标向下移动一行
while(resultSet.next()){
int id = resultSet.getInt(1);
String name = resultSet.getString("name");
double balance = resultSet.getDouble(3);
System.out.println(id "--" "name" "--" balance);
}
3.4.2 ResultSet进阶练习(查询的数据封装为对象)
【要求】:查询emp表中的数据,将其封装为对象,然后装载集合返回。
【实现思路】:
- 1)定义Emp类;
- 2)定义方法public List<Emp> findAll(){}
- 3)实现方法:select * from emp;
【源码】:其中,可以以findAll方法为模板,封装个自己的mysql的查询工具类
1)JdbcDemo.java
代码语言:javascript复制public class JdbcDemo4 {
public static void main(String[] args) {
//测试
List<Emp> list = new JdbcDemo4().findAll();
System.out.println(list.size());
System.out.println(list);
}
public List<Emp> findAll(){
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
List<Emp> list = null;
try {
//1、注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2、定义sql
String sql = "select * from emp";
//3、获取Connection对象
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/db1", "root", "root");
//4、获取执行sql对象
statement = connection.createStatement();
//5、执行sql
resultSet = statement.executeQuery(sql);
//6、处理结果
//遍历结果集,封装对象,装载集合
Emp emp = null;
list = new ArrayList<>();
while(resultSet.next()){
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
String gender = resultSet.getString("gender");
double salary = resultSet.getDouble("salary");
Date date = resultSet.getDate("join_date");
int dept_id = resultSet.getInt("dept_id");
emp = new Emp();
emp.setId(id);
emp.setName(name);
emp.setGender(gender);
emp.setSalary(salary);
emp.setJoin_date(date);
emp.setDept_id(dept_id);
list.add(emp);
}
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}finally {
//7、释放资源
//要避免空指针异常
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 list;
}
}
2)Emp.java 类
代码语言:javascript复制public class Emp {
private int id;
private String name;
private String gender;
private Double salary;
private Date join_date;
private int dept_id;
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 String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public Double getSalary() {
return salary;
}
public void setSalary(Double salary) {
this.salary = salary;
}
public Date getJoin_date() {
return join_date;
}
public void setJoin_date(Date join_date) {
this.join_date = join_date;
}
public int getDept_id() {
return dept_id;
}
public void setDept_id(int dept_id) {
this.dept_id = dept_id;
}
@Override
public String toString() {
return "Emp{"
"id=" id
", name='" name '''
", gender='" gender '''
", salary=" salary
", joindate=" join_date
", dept_id=" dept_id
'}';
}
}
3.4.3 抽取JdbcUtils工具类
由上一节我们可以发现,完成JDBC操作时,代码重复度特别高,每一次操作都要获取连接----释放资源,所以可以写一个工具类,来简化代码书写:
- 抽取一个方法注册驱动;
- 抽取一个方法获取连接对象; 需求:不传递参数,比较麻烦,还得保证工具类的通用性; 解决:通过配置文件解决此问题,提供一个文件jdbc.properties,定义url=...,user=...,password=...,读取配置文件即可获取参数,参数发生变化时只需该配置文件,无需改动代码。
- 抽取一个方法释放资源
【JdbcUtils工具类源码】
代码语言:javascript复制//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();
//2、加载文件
//动态获取src路径下的文件的方式 类加载器
ClassLoader loader = JdbcUtils.class.getClassLoader();
URL resource = loader.getResource("jdbc.properties");
String path = resource.getPath();
pro.load(new FileReader(path));
//3、获取属性赋值
url = pro.getProperty("url");
user = pro.getProperty("user");
password = pro.getProperty("password");
driver = pro.getProperty("driver");
//4、注册驱动
Class.forName(driver);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
/*
* 获取连接
*
* */
public static Connection getConnection() throws SQLException{
return DriverManager.getConnection(url,user,password);
}
/*
* 释放资源
*
* */
public static void close(Statement stmt,Connection conn){
if(stmt!=null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
/*
* 释放资源
*
* */
public static void close(ResultSet rs, Statement stmt, Connection conn){
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(stmt!=null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
【举例】:通过键盘录入用户名及密码,判断用户登录成功或失败
1)先准备user数据库表
代码语言:javascript复制CREATE TABLE USER(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20),
psd VARCHAR(20)
);
INSERT INTO USER VALUES(NULL,'zhangsan','123');
INSERT INTO USER VALUES(NULL,'lisi','456');
SELECT * FROM USER;
2)登录方法 代码及测试
代码语言:javascript复制public class JdbcDemo6 {
public static void main(String[] args) {
//键盘输入
Scanner scanner = new Scanner(System.in);
System.out.println("请输入用户名:");
String usr = scanner.nextLine();
System.out.println("请输入密码:");
String psd = scanner.nextLine();
//登录验证
Boolean b = login(usr,psd);
if(b){
System.out.println("登录OK");
}else{
System.out.println("登录fail");
}
}
public static boolean login(String user,String psd){
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
if(user == null || psd == null){
return false;
}
try {
connection = JdbcUtils.getConnection();
String sql = "select * from user where name = '" user "' and psd = '" psd "' ";
statement = connection.createStatement();
resultSet = statement.executeQuery(sql);
return resultSet.next();
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
JdbcUtils.close(resultSet,statement,connection);
}
return false;
}
}
3.5 PreparedStatement执行SQL的对象
1)SQL注入问题:在拼接sql时,有一些特殊的关键字参与字符的拼接,会造成安全性问题。
以上登录验证,实际上没有这么干的,因为质量太差,我们试验下,输入如下用户名及密码,看下结果如何:
随便输入的用户名和密码,竟然也登录成功了。以上的问题就是SQL注入问题,看打印出的sql语句,or 'a' = 'a',这是个恒等式,所以才会出现上述的问题。
2)解决SQL注入问题:使用PreparedStatement对象
预编译SQL:参数使用?作为占位符
使用步骤:
- 1)导入驱动jar包:mysql-connector-java-5.1.37-bin.jar;
- 2)编写代码,注册驱动;
- 3)获取数据库的连接对象;
- 4)定义sql语句; 注意:sql的参数使用?作为占位符,如select * from user where user= ? and psd = ?;
- 5)获取执行sql语句的对象PreparedStatement,Connection.prepareStatement(String sql);
- 6)给?赋值,使用PreparedStatement中的方法,setXxx(参数1,参数2);参数1 :?位置,从1 开始,参数2:?的值;
- 7)执行sql(不需要传递sql语句),接收返回结果;
- 8)处理结果;
- 9)释放资源,否则可能会造成内存的泄露问题。
【注意】:后期都会使用PrepareStatement对象完成增删改查的所有操作,可以防止SQL注入,且效率更高,不会再用Statement对象。
使用PrepareStatement完成上述的 登录验证案例如下:
代码语言:javascript复制public static boolean login2(String user,String psd){
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
if(user == null || psd == null){
return false;
}
try {
connection = JdbcUtils.getConnection();
String sql = "select * from user where name = ? and psd = ? ";
// System.out.println(sql);
statement = connection.prepareStatement(sql);
//给?赋值
statement.setString(1,user);
statement.setString(2,psd);
resultSet = statement.executeQuery();
return resultSet.next();
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
JdbcUtils.close(resultSet,statement,connection);
}
return false;
}
4 JDBC事务管理
关于事务可以看下这篇博客,使用JDBC控制事务,需要使用Connection对象来管理事务:
- 开启事务:setAutoCommit(boolean autoCommit),调用方法时参数为false,在执行sql之前开启事务;
- 提交事务:commit(),当所有sql都执行完提交事务;
- 回滚事务:rollback(),在catch中回滚事务。
【举例】:使用JDBC进行事务管理的示例,zhangsan -500,lisi 500,注意何时开启事务,何时提交事务,何时回滚事务。
代码语言:javascript复制public class JdbcDemo7 {
public static void main(String[] args) {
Connection connection = null;
PreparedStatement pstmt1 = null;
PreparedStatement pstmt2 = null;
try {
//1、获取连接
connection = JdbcUtils.getConnection();
connection.setAutoCommit(false);//开启事务
//2、定义sql
String sql1 = "update account set balance = balance - ? where id = ?";
String sql2 = "update account set balance = balance ? where id = ?";
//3、获取执行sql对象
pstmt1 = connection.prepareStatement(sql1);
pstmt2 = connection.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();
connection.commit(); //提交事务
} catch (Exception e) {
try {
if(connection != null){
connection.rollback();//事务回滚
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
e.printStackTrace();
}finally {
JdbcUtils.close(pstmt1,connection);
JdbcUtils.close(pstmt2,null);
}
}
}
———————————————————————————————————————
本文为博主原创文章,转载请注明出处!