JDBC

Posted onepg

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JDBC相关的知识,希望对你有一定的参考价值。

JDBC

1. Jdbc概述

问题:实际开发中,不可能用工具或者命令行操作数据库,数据库表中的数据最终要使用Java程序来操作,那么Java中如何操作数据库中的数据呢?

 

Java语言中,有一个专门连接数据库的规范(JDBC),专门负责连接数据库进行数据操作的规范

 

JDBC只是SUN编写的一堆接口(规范的体现),SUN公司自己并没有实现

 

 

问题 为什么SUN只定义一个JDBC规范,而不实现呢?

 

因为市面上的数据库很多,每个数据库内部接口不会向外暴露,而且即便是暴露让SUN去实现,市面上很多数据库全部要SUN来实现不现实

 

实际中哪个数据库需要支持JAVA语言,就需要自己实现JavaJDBC规范,因为实现了JDBC很多接口,那么就会有很多实现类,而很多实现类在java中会使用一个专门的包封装起来,叫做jar(在JDBC中叫做驱动包),各大数据库产商实现JDBC规范以后都会把他们jar包放在官网上以供开发者下载使用

 

1.1. JDBC

JDBC(Java DataBase Connectivity):

是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。JDBC提供了一种基

JDBC规范对应的api

 

 

2. 入门案例

2.1. 连接数据库

案例使用JDBC操作mysql数据库

 

2.2. 创建普通java项目

2.3. 在项目下面新建一个lib目录

 

2.4. MySQL驱动包拷贝到项目中并添加依赖

 

 

 

2.5. 获取数据库连接对象

准备:

1.拷贝MySQL的驱动包到项目中去:mysql-connector-java-5.1.x-bin.jar

2.build path,告速项目去哪里去找字节码文件.

--------------------------------------------------------------------------------

操作JDBC的第一步,获取JDBC的连接对象.:Connection.

 

步骤:

  1.加载注册驱动.

    就是把驱动中的Driver字节码加载到JVM.

   Class.forName("com.mysql.jdbc.Driver");

   为什么这句话就可以加载注册驱动?

   第一步:com.mysql.jdbc.Driver.class这份字节码加载到JVM.

   第二步:当一份字节码被加载进JVM,马上就会执行该字节码中的静态代码块.

    第三步:该静态代码中,就在完成,先创建驱动对象,再注册.

  2.通过DriverManager获取连接对象.

    public static Connection getConnection(String url,String user,String password)

 

Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/dbName","root","admin");

 

jdbc:mysql://localhost:3306/dbName

jdbc:mysql:// :连接MySQL数据库的协议,不同数据库协议不一样

localhost:3306 :数据库软件的主机和端口

dbName : 具体要连接数据库

    若数据库安装在本机,并且端口是默认的3306,则可以简写:

    Connection conn = DriverManager.getConnection("jdbc:mysql:///dbName","root","admin");

 

验证已经获取连接:可以在MySQL控制台,使用命令:show processlist; 查看MySQL运行进程.

 

 

 

public class GetConnectionDemo {

public static void main(String[] args) throws Exception {

 

//1.加载注册驱动 : 把当前类对应的字节码加载到JVM中

Class.forName("com.mysql.jdbc.Driver");

//2.获取数据库连接

Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");

System.out.println(conn);

 

}

}

 

3. 创建表-DDL操作

在其他操作之间先要把数据库表要创建出来

 

创建一张t_student:

id:

name:

age:

/*

 *

 * 创建表操作

 * SQL : create table t_student (id int primary key auto_increment,name varchar(50),age int)

 */

 

public static void main(String[] args) throws Exception {

String sql = "create table t_student (id int primary key auto_increment,name varchar(50),age int)";

//贾琏欲执事

//1,加载注册驱动

Class.forName("com.mysql.jdbc.Driver");

//2,获取数据库连接

Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");

//3,创建语句对象(用于执行SQL语句的对象)

Statement st = conn.createStatement();

//4, 执行SQL语句

//int rows = st.executeUpdate(String sql);执行DDL和DML语句,放回的是受影响的行数

//ResultSet res = st.executeQuery(String sql);执行DQL查询语句,返回的结果集对象

st.executeUpdate(sql);

//5,释放资源(先开后关)

st.close();

conn.close();

}

 

4. DML操作-表数据的增删改

//DML : 对表数据的增删改操作

public class DMLDemo {

 

/*

 * 向 t_student表中插入一条数据

 * sql : insert into t_student(name,age) values (‘乔峰‘,30)

 */

@Test

public void testInsert() throws Exception {

String sql = "insert into t_student(name,age) values (‘乔峰‘,30)";

 

// 1.加载注册驱动

Class.forName("com.mysql.jdbc.Driver");

 

// 2.获取数据库连接对象

Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");

// 3.创建语句对象

Statement st = conn.createStatement();

 

// 4.执行SQL语句

// int rows = st.executeUpdate(String sql);执行DDL和DML语句,放回的是受影响的行数

// ResultSet res = st.executeQuery(String sql);执行DQL查询语句,返回的结果集对象

int rows = st.executeUpdate(sql);

System.out.println(rows);

//5.释放资源(先开后关)

st.close();

conn.close();

 

}

/*

 * 删除操作: 删除t_student表中的某一条数据

 * SQL :delete from t_student where id = 2

 */

@Test

public void testDelete() throws Exception {

String sql = "delete from t_student where id = 2";

 

// 1.加载注册驱动

Class.forName("com.mysql.jdbc.Driver");

 

// 2.获取数据库连接对象

Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");

// 3.创建语句对象

Statement st = conn.createStatement();

 

// 4.执行SQL语句

// int rows = st.executeUpdate(String sql);执行DDL和DML语句,放回的是受影响的行数

// ResultSet res = st.executeQuery(String sql);执行DQL查询语句,返回的结果集对象

int rows = st.executeUpdate(sql);

System.out.println(rows);

//5.释放资源(先开后关)

st.close();

conn.close();

}

/*

 * 修改操作 : 修改t_student表中的某一条数据

 * SQL : update t_student set name = ‘虚竹‘,age = 50 where id = 3

 */

@Test

public void testUpdate() throws Exception {

String sql = "update t_student set name = ‘虚竹‘,age = 50 where id = 3";

 

// 1.加载注册驱动

Class.forName("com.mysql.jdbc.Driver");

 

// 2.获取数据库连接对象

Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");

// 3.创建语句对象

Statement st = conn.createStatement();

 

// 4.执行SQL语句

// int rows = st.executeUpdate(String sql);执行DDL和DML语句,放回的是受影响的行数

// ResultSet res = st.executeQuery(String sql);执行DQL查询语句,返回的结果集对象

int rows = st.executeUpdate(sql);

System.out.println(rows);

//5.释放资源(先开后关)

st.close();

conn.close();

}

}

 

5. DQL操作-查询操作

5.1. 查询操作的分析

 

 

 

5.2. 查询具体操作

结果集的列的位置

 

 

  1. 使用 rs.next() 偏移光标,循环指定具体的某一行

 

获取数据的具体方法

 Object

getObject(int columnIndex) 
 columnIndex : 通过结果集的位置(1开始)获取对应的数据

 Object

getObject(String columnLabel) 
columnLabel : 通过结果集的 列名获取对应的数据

 

package cn.sxt.jdbc._01connection;

 

 

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.ResultSet;

import java.sql.Statement;

import java.util.ArrayList;

import java.util.List;

 

import org.junit.Test;

 

//DQL :查询操作

public class D_DQLDemo {

 

/*

 * 多行查询 :查询t_student表中的所有数据

 * SQL : select * from t_student

 */

@Test

public void testList() throws Exception {

String sql = "select * from t_student";

 

// 1.加载注册驱动

Class.forName("com.mysql.jdbc.Driver");

 

// 2.获取数据库连接对象

Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");

// 3.创建语句对象

Statement st = conn.createStatement();

 

// 4.执行SQL语句

// int rows = st.executeUpdate(String sql);执行DDL和DML语句,放回的是受影响的行数

// ResultSet res = st.executeQuery(String sql);执行DQL查询语句,返回的结果集对象

ResultSet rs = st.executeQuery(sql);

 

 

//创建list集合用于封装Student对象

List<Student> stus = new ArrayList<>();

 

while(rs.next()) {

//1.通过结果集的位置获取对应的数

/*Object id = rs.getObject(1);

Object name = rs.getObject(2);

Object age = rs.getObject(3);*/

 

//2.通过结果集的 列名获取对应的数据

/*Object id = rs.getObject("id");

Object name = rs.getObject("name");

Object age = rs.getObject("age");*/

//3.通过数据库数据和Java对应的数据类型获取对应的只

int id = rs.getInt("id");

String name = rs.getString("name");

int age = rs.getInt("age");

//System.out.println(id+","+name+","+age);

 

//将获取的数据封装成对应的Student对象

Student stu = new  Student(id, name, age);

 

//将一个个Student对象添加到list集合中

stus.add(stu);

}

 

for (Student student : stus) {

System.out.println(student);

}

//5.释放资源(先开后关)

rs.close();

st.close();

conn.close();

}

 

 

/*

 * 单行查询: 查询出t_student 指定id的信息

 * SQL : select * from t_student where id = 1;

 */

@Test

public void testGetOne() throws Exception {

String sql = "select * from t_student where id = 2";

 

// 1.加载注册驱动

Class.forName("com.mysql.jdbc.Driver");

 

// 2.获取数据库连接对象

Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");

// 3.创建语句对象

Statement st = conn.createStatement();

 

// 4.执行SQL语句

// int rows = st.executeUpdate(String sql);执行DDL和DML语句,放回的是受影响的行数

// ResultSet res = st.executeQuery(String sql);执行DQL查询语句,返回的结果集对象

ResultSet rs = st.executeQuery(sql);

 

if(rs.next()) {

//1.通过结果集的位置获取对应的数

/*Object id = rs.getObject(1);

Object name = rs.getObject(2);

Object age = rs.getObject(3);*/

 

//2.通过结果集的 列名获取对应的数据

/*Object id = rs.getObject("id");

Object name = rs.getObject("name");

Object age = rs.getObject("age");*/

//3.通过数据库数据和Java对应的数据类型获取对应的只

int id = rs.getInt("id");

String name = rs.getString("name");

int age = rs.getInt("age");

//System.out.println(id+","+name+","+age);

 

//将获取的数据封装成对应的Student对象

Student stu = new  Student(id, name, age);

System.out.println(stu);

}

//5.释放资源(先开后关)

rs.close();

st.close();

conn.close();

}

}

 

 

6. 预编译语句对象PreparedStatment

问题 我们有了Statment对象可以执行SQL,为什么还要使用PreparedStatment

 

优势

  1. SQL语句结构清晰,参数的设置和SQL语句分离
  2. 性能更高
  3. 防止SQL注入

 

Statement: 表示静态SQL语句对象.

PreparedStatement:Statement的子接口,表示预编译SQL语句对象. 通过占位符(?)来拼SQL.  

6.1. 创建PreparedStatement

创建语句对象 Statment

 Statement

createStatement() 
创建一个 Statement 对象来将 SQL 语句发送到数据库。

 

创建预编译语句对象PreparedStatement

 PreparedStatement

prepareStatement(String sql) 
创建预编译语句对象,SQL是带有占位符的SQL模板.

 

 

 

6.2. 执行SQL语句的方法

6.2.1. Statment

在执行SQL语句的时候回带上SQL语句

 ResultSet

executeQuery(String sql) 
          执行给定的 SQL 语句,该语句返回单个 ResultSet 对象。

 int

executeUpdate(String sql) 
          执行给定 SQL 语句,该语句可能为 INSERT、UPDATE 或 DELETE 语句,或者不返回任何内容的 SQL 语句(如 SQL DDL 语句)。

 

6.2.2. PreparedStatement 

在执行SQL语句的方法中不需要设置SQL语句

 ResultSet

executeQuery() 
          在此 PreparedStatement 对象中执行 SQL 查询,并返回该查询生成的 ResultSet 对象。

 int

executeUpdate() 
          在此 PreparedStatement 对象中执行 SQL 语句,该语句必须是一个 SQL 数据操作语言(Data Manipulation Language,DML)语句,比如 INSERT、UPDATE 或 DELETE 语句;或者是无返回内容的 SQL 语句,比如 DDL 语句。

 

 

6.3. 设置站位参数的值

void  setXxx(int parameterIndex,Xxx value):用于设置占位符参数,

       parameterIndex:第几个问号. 注意:1开始.

       value:设置的真实值.

Xxx:表示数据类型.String/int/long/Double

 

6.4. 代码

package cn.sxt.jdbc._01connection;

 

import static org.junit.Assert.*;

 

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.PreparedStatement;

 

import org.junit.Test;

 

//DML : 对表数据的增删改操作,使用预编译语句对象

public class E_DMLByPreparedStatmentDemo {

 

/*

 * 向 t_student表中插入一条数据

 * sql : insert into t_student(name,age) values (‘乔峰‘,30)

 */

@Test

public void testInsert() throws Exception {

String sql = "insert into t_student(name,age) values (?,?)";

 

// 1.加载注册驱动

Class.forName("com.mysql.jdbc.Driver");

 

// 2.获取数据库连接对象

Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");

// 3.创建预编译语句对象

PreparedStatement ps = conn.prepareStatement(sql);

//3.1设置占位符参数

ps.setString(1, "东方姑娘");

ps.setInt(2, 18);

 

// 4.执行SQL语句:注意不要带SQL参数

ps.executeUpdate();

//5.释放资源(先开后关)

ps.close();

conn.close();

 

}

/*

 * 删除操作: 删除t_student表中的某一条数据

 * SQL :delete from t_student where id = 2

 */

@Test

public void testDelete() throws Exception {

String sql = "delete from t_student where id = ?";

 

// 1.加载注册驱动

Class.forName("com.mysql.jdbc.Driver");

 

// 2.获取数据库连接对象

Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");

// 3.创建预编译语句对象

PreparedStatement ps = conn.prepareStatement(sql);

//3.1设置占位符对应的参数值

ps.setInt(1, 1);

 

// 4.执行SQL语句

int rows = ps.executeUpdate();

System.out.println(rows);

//5.释放资源(先开后关)

ps.close();

conn.close();

}

/*

 * 修改操作 : 修改t_student表中的某一条数据

 * SQL : update t_student set name = ‘虚竹‘,age = 50 where id = 3

 */

@Test

public void testUpdate() throws Exception {

String sql = "update t_student set name = ?,age = ? where id = ?";

 

// 1.加载注册驱动

Class.forName("com.mysql.jdbc.Driver");

 

// 2.获取数据库连接对象

Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");

// 3.创建预编译语句对象

PreparedStatement ps = conn.prepareStatement(sql);

//3.1设置占位符参数对应的值

ps.setString(1, "西方失败");

ps.setInt(2, 40);

ps.setInt(3, 4);

// 4.执行SQL语句

int rows = ps.executeUpdate();

System.out.println(rows);

//5.释放资源(先开后关)

ps.close();

conn.close();

}

}

 

 

7. DAO设计

实际开发中,JavaWeb开发代码一般分为三层,分层结构是JavaWeb开发中的一种设计思想,这样会让我们开发层次分明,每一层只要完成对应的功能即可,使得项目便于开发和维护

 

1 . Web/表现层 : 主要接受前台浏览器用户的参数,给浏览器响应数据等等

  1. Service/业务成:主要处理业务功能,日志,权限,事物,等等
  2. DAO/持久层 :专门负责和数据库交互,数据处理相关代码

 

 

DAO Data Access Object 数据访问对象

 

 

7.1. DAO思想

 

 

7.2. 使用DAO以后代码的以及包的设计结构

开发中如果使用的分层,编写的包和类名接口名等等都是有固定规则,不能随便瞎写

7.2.1. DAO层接口包命名

 

公司域名倒写+项目名称/模块名称+dao

如 : cn.sxt.crm.dao

7.2.2. DAO层实现类包命名

 

公司域名倒写+项目名称/模块名称+dao+impl

如 : cn.sxt.crm.dao.impl

 

7.2.3. DAO层操作对应表的接口命名

 

对应表的名称 + Dao/DAO

 

如 : StudentDao/DAO , TeacherDao/DAO

 

7.2.4. DAO层操作对应表的实现类命名

 

对应表的名称 + Dao/DAOImpl

 

如 : StudentDaoImpl/DAOImpl , TeacherDaoImpl/DAOImpl

7.2.5. 数据表对应的Javadomain/pojo包命名
POJOPlain Ordinary Java Object)简单的Java对象

 

公司域名倒写+项目名称/模块名称+domain/pojo

如 : cn.sxt.crm.domain

 

7.2.6. 对应的测试包命名

公司域名倒写+项目名称/模块名称+test

如 : cn.sxt.crm.test

7.2.7. 项目的工具类包命名

公司域名倒写+项目名称/模块名称+util/utils

如 : cn.sxt.crm.util/utils

 

 

7.2.8. DAO代码设计结构

 

 

7.2.9. Dao的实现类代码

package cn.sxt.jdbc.dao.impl;

 

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.SQLException;

import java.util.ArrayList;

import java.util.List;

 

import cn.sxt.jdbc.dao.StudentDao;

import cn.sxt.jdbc.domain.Student;

 

public class StudentDaoImpl implements StudentDao {

 

@Override

public int saveStudent(Student stu) {

String sql = "insert into t_student(name,age) values (?,?)";

 

Connection conn = null;

PreparedStatement ps = null;

try {

 

// 1.加载注册驱动

Class.forName("com.mysql.jdbc.Driver");

 

// 2.获取数据库连接对象

conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");

// 3.创建预编译语句对象

ps = conn.prepareStatement(sql);

//3.1设置占位符参数

ps.setString(1, stu.getName());

ps.setInt(2, stu.getAge());

 

// 4.执行SQL语句:注意不要带SQL参数

return ps.executeUpdate();

 

 

} catch (Exception e) {

e.printStackTrace();

}finally {

//5.释放资源(先开后关)

try {

if(ps !=null) {

ps.close();

}

} catch (SQLException e) {

e.printStackTrace();

}finally {

try {

if(conn !=null) {

conn.close();

}

} catch (SQLException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

return 0;

}

 

@Override

public int deleteById(int id) {

 

String sql = "delete from t_student where id = ?";

 

Connection conn = null;

PreparedStatement ps = null;

try {

 

// 1.加载注册驱动

Class.forName("com.mysql.jdbc.Driver");

 

// 2.获取数据库连接对象

conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");

// 3.创建预编译语句对象

ps = conn.prepareStatement(sql);

//3.1设置占位符参数

ps.setInt(1, id);

 

// 4.执行SQL语句:注意不要带SQL参数

return ps.executeUpdate();

 

 

} catch (Exception e) {

e.printStackTrace();

}finally {

//5.释放资源(先开后关)

try {

if(ps !=null) {

ps.close();

}

} catch (SQLException e) {

e.printStackTrace();

}finally {

try {

if(conn !=null) {

conn.close();

}

} catch (SQLException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

return 0;

}

 

@Override

public int updateStudentById(Student stu) {

 

String sql = "update t_student set name = ?,age = ? where id = ?";

 

Connection conn = null;

PreparedStatement ps = null;

try {

 

// 1.加载注册驱动

Class.forName("com.mysql.jdbc.Driver");

 

// 2.获取数据库连接对象

conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");

// 3.创建预编译语句对象

ps = conn.prepareStatement(sql);

//3.1设置占位符参数

ps.setString(1, stu.getName());

ps.setInt(2, stu.getAge());

ps.setInt(3, stu.getId());

// 4.执行SQL语句:注意不要带SQL参数

return ps.executeUpdate();

 

 

} catch (Exception e) {

e.printStackTrace();

}finally {

//5.释放资源(先开后关)

try {

if(ps !=null) {

ps.close();

}

} catch (SQLException e) {

e.printStackTrace();

}finally {

try {

if(conn !=null) {

conn.close();

}

} catch (SQLException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

return 0;

}

 

@Override

public Student selectById(int id) {

String sql = "select * from t_student where id = ?";

 

Connection conn = null;

PreparedStatement ps = null;

ResultSet rs = null;

 

try {

// 1.加载注册驱动

Class.forName("com.mysql.jdbc.Driver");

 

// 2.获取数据库连接对象

conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");

// 3.创建语句对象

ps = conn.prepareStatement(sql);

//3.1设置占位符参数对应的值

ps.setInt(1, id);

 

// 4.执行SQL语句

rs = ps.executeQuery();

if(rs.next()) {

//通过数据库数据和Java对应的数据类型获取对应的只

String name = rs.getString("name");

int age = rs.getInt("age");

//System.out.println(id+","+name+","+age);

 

//将获取的数据封装成对应的Student对象

Student stu = new  Student(id, name, age);

 

return stu;

}

 

} catch (Exception e) {

// TODO: handle exception

}finally {

try {

if(rs !=null) {

rs.close();

}

} catch (SQLException e) {

e.printStackTrace();

}finally {

try {

if(ps !=null) {

ps.close();

}

} catch (SQLException e) {

e.printStackTrace();

}finally {

try {

if(conn !=null) {

conn.close();

}

} catch (SQLException e) {

e.printStackTrace();

}

}

}

}

 

return null;

}

 

@Override

public List<Student> selectList() {

String sql = "select * from t_student";

//创建list集合用于封装Student对象

List<Student> stus = new ArrayList<>();

 

Connection conn = null;

PreparedStatement ps = null;

ResultSet rs = null;

 

try {

 

// 1.加载注册驱动

Class.forName("com.mysql.jdbc.Driver");

 

// 2.获取数据库连接对象

conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");

// 3.创建语句对象

ps = conn.prepareStatement(sql);

 

// 4.执行SQL语句

rs = ps.executeQuery();

while(rs.next()) {

//通过数据库数据和Java对应的数据类型获取对应的只

int id = rs.getInt("id");

String name = rs.getString("name");

int age = rs.getInt("age");

//System.out.println(id+","+name+","+age);

 

//将获取的数据封装成对应的Student对象

Student stu = new  Student(id, name, age);

//将一个个Student对象添加到list集合中

stus.add(stu);

}

 

} catch (Exception e) {

// TODO: handle exception

}finally {

try {

if(rs !=null) {

rs.close();

}

} catch (SQLException e) {

e.printStackTrace();

}finally {

try {

if(ps !=null) {

ps.close();

}

} catch (SQLException e) {

e.printStackTrace();

}finally {

try {

if(conn !=null) {

conn.close();

}

} catch (SQLException e) {

e.printStackTrace();

}

}

}

}

 

return stus;

}

 

}

 

 

7.3. 代码初步重构

上述的DAO方法中的代码,存在的问题:

 

问题1:每个DAO方法中都会写:驱动名称/url/账号/密码,不利于维护.

    解决方案: 声明为成员变量即可.(在被类中任何地方都可以访问)

 

问题2:问题1的解决方案有问题.

    每个DAO实现类里都有一模一样的4行代码,不利于维护(考虑有100个DAO实现类,就得重复99次).

    解决方案: 把驱动名称/url/账号/密码这四行代码,专门抽取到一个JDBC的工具类中.---->JdbcUtil.

问题3:其实DAO方法,每次操作都只想需要Connection对象即可,而不关心是如何创建的.

    解决方案:把创建Connection的代码,抽取到JdbcUtil中,并提供方法getConn用于向调用者返回Connection对象即可.

问题4:每次调用者调用getConn方法的时候,都会创建一个Connection对象.

      但是,每次都会加载注册驱动一次.--->没必要的.

      解决方案:把加载注册驱动的代码放在静态代码块中--->只会在所在类被加载进JVM的时候,执行一次.

问题5:每个DAO方法都要关闭资源.(鸡肋代码).

      解决方案:把关闭资源的代码,抽取到JdbcUtil中.

      public static void close(Connection conn, Statement st, ResultSet rs) {}

      调用者:

        DML:  JdbcUtil.close(conn,st,null);

        DQL:  JdbcUtil.close(conn,st,rs);

问题6 :连接数据库的账号密码写死在JdbcUtil工具类中了,不利于维护

     抽取 db.properties 配置文件,将数据库对应的账号密码写到配置文件中,然后使用程序读取配置文件内容即可

 

 

7.3.1. JdbcUtil工具类

package cn.sxt.jdbc.util;

 

import java.io.InputStream;

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.SQLException;

import java.util.Properties;

 

 

public class JdbcUtil {

 

// alt+shif+a 多行修改,修改以后还原 alt+shif+a

 

/*private static String driverClassName = "com.mysql.jdbc.Driver";

private static String url = "jdbc:mysql://localhost:3306/jdbcdemo";

private static String username = "root";

private static String password = "root";*/

 

private static Properties p = new Properties();

 

static {

try {

//1.获取类加载器

ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

//2,使用类加载器获取项目 类路径下面的文件

InputStream inputStream = classLoader.getResourceAsStream("db.properties");

 

//3.使用Priperties加载配置文件对应的输入流

p.load(inputStream);

 

Class.forName(p.getProperty("driverClassName"));

} catch (Exception e) {

e.printStackTrace();

}

}

 

public static Connection getConnection() {

try {

 

return DriverManager.getConnection(p.getProperty("url"), p.getProperty("username"), p.getProperty("password"));

} catch (Exception e) {

e.printStackTrace();

throw new RuntimeException("亲,连接数据库失败", e);

}

}

 

public static void close(Connection conn,PreparedStatement ps,ResultSet rs) {

try {

if(rs !=null) {

rs.close();

}

} catch (SQLException e) {

e.printStackTrace();

}finally {

try {

if(ps !=null) {

ps.close();

}

} catch (SQLException e) {

e.printStackTrace();

}finally {

try {

if(conn !=null) {

conn.close();

}

} catch (SQLException e) {

e.printStackTrace();

}

}

}

}

}

 

7.3.2. 使用工具类以后的DAO实现类效果

package cn.sxt.jdbc.dao.impl;

 

import java.sql.Connection;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.SQLException;

import java.util.ArrayList;

import java.util.List;

 

import cn.sxt.jdbc.dao.StudentDao;

import cn.sxt.jdbc.domain.Student;

import cn.sxt.jdbc.util.JdbcUtil;

 

public class StudentDaoImpl implements StudentDao {

 

 

 

@Override

public int saveStudent(Student stu) {

String sql = "insert into t_student(name,age) values (?,?)";

 

Connection conn = null;

PreparedStatement ps = null;

try {

conn = JdbcUtil.getConnection();

 

// 3.创建预编译语句对象

ps = conn.prepareStatement(sql);

//3.1设置占位符参数

ps.setString(1, stu.getName());

ps.setInt(2, stu.getAge());

 

// 4.执行SQL语句:注意不要带SQL参数

return ps.executeUpdate();

 

} catch (Exception e) {

e.printStackTrace();

}finally {

JdbcUtil.close(conn, ps, null);

}

return 0;

}

 

@Override

public int deleteById(int id) {

 

String sql = "delete from t_student where id = ?";

 

Connection conn = null;

PreparedStatement ps = null;

try {

 

conn = JdbcUtil.getConnection();

// 3.创建预编译语句对象

ps = conn.prepareStatement(sql);

//3.1设置占位符参数

ps.setInt(1, id);

 

// 4.执行SQL语句:注意不要带SQL参数

return ps.executeUpdate();

 

 

} catch (Exception e) {

e.printStackTrace();

}finally {

JdbcUtil.close(conn, ps, null);

}

return 0;

}

 

@Override

public int updateStudentById(Student stu) {

 

String sql = "update t_student set name = ?,age = ? where id = ?";

 

Connection conn = null;

PreparedStatement ps = null;

try {

 

conn = JdbcUtil.getConnection();

// 3.创建预编译语句对象

ps = conn.prepareStatement(sql);

//3.1设置占位符参数

ps.setString(1, stu.getName());

ps.setInt(2, stu.getAge());

ps.setInt(3, stu.getId());

// 4.执行SQL语句:注意不要带SQL参数

return ps.executeUpdate();

 

 

} catch (Exception e) {

e.printStackTrace();

}finally {

JdbcUtil.close(conn, ps, null);

}

return 0;

}

 

@Override

public Student selectById(int id) {

String sql = "select * from t_student where id = ?";

 

Connection conn = null;

PreparedStatement ps = null;

ResultSet rs = null;

 

try {

conn = JdbcUtil.getConnection();

// 3.创建语句对象

ps = conn.prepareStatement(sql);

//3.1设置占位符参数对应的值

ps.setInt(1, id);

 

// 4.执行SQL语句

rs = ps.executeQuery();

if(rs.next()) {

//通过数据库数据和Java对应的数据类型获取对应的只

String name = rs.getString("name");

int age = rs.getInt("age");

//System.out.println(id+","+name+","+age);

//将获取的数据封装成对应的Student对象

Student stu = new  Student(id, name, age);

return stu;

}

 

} catch (Exception e) {

// TODO: handle exception

}finally {

try {

if(rs !=null) {

rs.close();

}

} catch (SQLException e) {

e.printStackTrace();

}finally {

JdbcUtil.close(conn, ps, rs);

}

}

 

return null;

}

 

@Override

public List<Student> selectList() {

String sql = "select * from t_student";

//创建list集合用于封装Student对象

List<Student> stus = new ArrayList<>();

 

Connection conn = null;

PreparedStatement ps = null;

ResultSet rs = null;

 

try {

 

conn = JdbcUtil.getConnection();

// 3.创建语句对象

ps = conn.prepareStatement(sql);

 

// 4.执行SQL语句

rs = ps.executeQuery();

while(rs.next()) {

//通过数据库数据和Java对应的数据类型获取对应的只

int id = rs.getInt("id");

String name = rs.getString("name");

int age = rs.getInt("age");

//System.out.println(id+","+name+","+age);

 

//将获取的数据封装成对应的Student对象

Student stu = new  Student(id, name, age);

//将一个个Student对象添加到list集合中

stus.add(stu);

}

 

} catch (Exception e) {

// TODO: handle exception

}finally {

JdbcUtil.close(conn, ps, rs);

}

 

return stus;

}

 

}

 

 

7.4. 知识点补充,类加载器

如何使用类加载器加载配置文件

 

7.4.1. 配置文件

 

7.4.1.1. 配置文件创建的位置

配置文件一般都放在项目的src 源目录下面

 

 

7.4.2. 加载代码

package cn.sxt.jdbc.test;

 

import static org.junit.Assert.*;

 

import java.io.InputStream;

import java.util.Properties;

 

import org.junit.Test;

 

public class PropertiesTest {

 

@Test

public void testName() throws Exception {

 

 /*

  * ClassLoader 类加载器

  * ClassLoader :可以从项目的类路径下面读取对应的配置文件返回一个输入流

  * ClassLoader 在程序运行的时候JVM已经为每一个项目都创建了一个,我们开发者只需要获取即可

  * 获取类加载器方式

  * 1、使用当前线程

  * ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

  * 2、通过某一类的字节码实例也可以获取

  * ClassLoader classLoader = PropertiesTest.class.getClassLoader();

  */

ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

//使用类加载器获取项目 类路径下面的文件

InputStream inputStream = classLoader.getResourceAsStream("db.properties");

 

 

/*

 * Properties 是Map集合下面的一个 专门用于读取配置文件的对象

 * 可以读取当前类路径下面的  xxx.properites类型的配置文件

 *

 * xxx.properites的内容必须是key=value 键值对的数据

 */

 

//1.创建Properties对象

Properties p = new Properties();

 

//2.加载配置文件

p.load(inputStream);

 

System.out.println(p);

 

//获取具体某一个key对应的值

String driverClassName = p.getProperty("driverClassName");

System.out.println(driverClassName);

}

}

 

7.4.3. 效果

 

 

8. 连接池

8.1. 遇到的问题-引出连接池

 

 

8.2. 连接池思想

 

 

8.3. 连接池的概述

Java,连接池使用javax.sql.DataSource接口来表示连接池.

 

注意:DataSource仅仅只是一个接口,由各大服务器厂商来实现(Tomcat.JBoss).

常用的DataSource的实现:

  DBCP:  Spring推荐的

  C3P0:  Hibernate推荐的

  Druid : 阿里巴巴开源的,性能最好,速度最快

DataSource(数据源)和连接池(Connection Pool)是同一个.

 

8.4. 使用连接池和不使用连接池的区别在哪里

从代码上:

不使用连接池: Conenction对象由DriverManager获取.

  Connection conn = DriverManager.getConnection(url,username,password);

 

使用连接池:

  如何创建DataSource对象,如何在DataSource中设置url,账号,密码.

  Connection conn = DataSource对象.getConnection();

--------------------------------------------------------------------

使用连接池的时候:

  释放资源: Connection对象.close():

  是把Connection放回给连接池,而不是和数据库断开.

 

8.5. Druid连接池的使用

8.5.1. 准备druid 连接池jar包到项目

 

 

package cn.sxt.jdbc.test;

 

import static org.junit.Assert.*;

 

import java.io.InputStream;

import java.io.Reader;

import java.sql.Connection;

import java.util.Properties;

 

import javax.sql.DataSource;

 

import org.junit.Test;

 

import com.alibaba.druid.pool.DruidDataSource;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import com.alibaba.druid.pool.DruidPooledConnection;

 

public class DataSourceTest {

// 直接创建连接池对象

@Test

public void testName() throws Exception {

// 1.创建连接池对象

DruidDataSource ds = new DruidDataSource();

// 2.设置连接数据库的账号密码

ds.setDriverClassName("com.mysql.jdbc.Driver");

ds.setUrl("jdbc:mysql://localhost:3306/jdbcdemo");

ds.setUsername("root");

ds.setPassword("root");

ds.setMaxActive(10);// 最大连接数

// 3.获取连接对象

Connection conn = ds.getConnection();

System.out.println(conn);

}

 

// 使用工厂对象创建连接池对象,工厂对象的好处,不需要直接设置账号密码等等,只需要将

// 连接数据库的账号密码等等以指定的 key的名称配置到 xxx.properties文件中即可,工厂对象底层自动读取

@Test

public void testDataSourceByFactory() throws Exception {

 

// 1.获取类加载器用于加载clsspath下面的 配置文件

ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

// 2.读取druid.properties配置文件

InputStream inputStream = classLoader.getResourceAsStream("druid.properties");

// 3.创建Properties对象,并读取配置文件对应的输入流

Properties p = new Properties();

p.load(inputStream);

 

// 4.创建连接池对象

DataSource ds = DruidDataSourceFactory.createDataSource(p);

// 5.获取连接对象

Connection conn = ds.getConnection();

System.out.println(conn);

}

}

 

 

 

8.5.2. druid.propperties

 

 

8.5.3. 使用Druid抽取的工具类

 

 

9. 事务

案例:银行转账:从张无忌账户上给赵敏转1000.

准备:account(账户表):

---------------------------------------------------------------

id            name(账号,唯一)           balance(余额)

1             张无忌                    20000

2             赵敏                      0

---------------------------------------------------------------

转账的思路:

   1.检查张无忌的账号余额是否大于等于1000.

        SQL: SELECT balance FROM account WHERE name = ‘张无忌‘ AND balance >=1000

        余额>=1000:GOTO 2:

        余额 <1000:提示:,你的余额不足.

   2.在张无忌的账号余额上减少1000.

        SQL: UPDATE account SET balance = balance-1000 WHERE name = ‘张无忌

   3.在赵敏的账户余额尚增加1000.

        SQL: UPDATE account SET balance = balance+1000 WHERE name = ‘赵敏

-------------------------------------------------------------------------------------------

注意:在第二步和第三步之间,停电了.

     使用异常模拟停电:System.out.println(1/0);

9.1. 事务概述

 

事务(Transaction,简写为tx):

在数据库中,所谓事务是指一组逻辑操作单元,使数据从一种状态变换到另一种状态。

为确保数据库中数据的一致性,数据的操纵应当是离散的成组的逻辑单元:

    当每个逻辑操作单元全部完成时,数据的一致性可以保持,

    而当这个单元中的一部分操作失败,整个事务应全部视为错误,所有从起始点以后的操作应全部回退到开始状态。

 

事务的操作:先定义开始一个事务,然后对数据作修改操作,这时如果提交(commit),这些修改就永久地保存下来,如果回退(rollback),数据库管理系统将放弃您所作的所有修改而回到开始事务时的状态。

 

--------------------------------------------------

事务的ACID属性:

1. 原子性(Atomicity
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。 

2. 一致性(Consistency
事务必须使数据库从一个一致性状态变换到另外一个一致性状态。(数据不被破坏)

3. 隔离性(Isolation

事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。

4. 持久性(Durability
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响

 

--------------------------------------------------

事务:指构成单个逻辑工作单元的操作集合

事务处理:保证所有事务都作为一个工作单元来执行,即使出现了故障,都不能改变这种执行方式。当在一个事务中执行多个操作时,要么所有的事务都被提交(commit),要么整个事务回滚(rollback)到最初状态

处理事务的两个动作:

       提交:commit:   当整个事务中,所有的逻辑单元都正常执行成功.  ---->提交事务.---数据已经提交,不能更改.

       回滚:rollback: 当整个事务中,有一个逻辑单元执行失败,        ---->回滚事务.  

                      撤销该事务中的所有操作--->恢复到最初的状态.

 

---------------------------------------------------------------------------------------------------

如何在代码中去处理事务:

1.JDBC,事务是默认自动提交的.  必须先设置事务为手动提交.

connection对象.setAutoCommit(false);//设置事务为手动提交.

2.手动的提交事务.

connection对象.commit();

3.若出现异常必须回滚事务:

   不回滚事务,总余额依然是正确的. 若不回滚事务,不会释放数据库资源.

   connection对象.rollback();

-----------------------------------------------------------------------------------

1.JDBC在事务是默认提交的,那是在什么时候提交的.

在执行一个DML/DDL操作的时候,就已经提交事务了.

2.针对于CRUD操作. 只有DML操作才有事务,查询操作没有事务.

  但是,我们一般会把查询也放在事务里面.

  1. 以后,凡是发现自己编写的代码是正确的,测试也通过,但是就是数据库表中的数据不变----->事务没提交的问题.

 

 

4.MySQL,InnoDB支持外键.支持事务,MyISAM不支持外键,不支持事务.

 

 

9.2. 事务处理代码

public class TransactionTest {

@Test

public void testName() throws Exception {

Connection conn = null;

Statement st = null;

ResultSet rs = null;

try {

conn = DruidUtil.getConnection();

//将事务设置为手动提交

conn.setAutoCommit(false);

 

st = conn.createStatement();

// 1.检查张无忌的账号余额是否大于等于1000.

rs = st.executeQuery("SELECT balance FROM account WHERE name = ‘张无忌‘ AND balance >=1000");

if(!rs.next()) {

throw new RuntimeException("亲,您的账户余额不够");

}

// 余额>=1000:GOTO 2:

// 余额 <1000:提示:亲,你的余额不足.

// 2.在张无忌的账号余额上减少1000.

st.executeUpdate("UPDATE account SET balance = balance-1000 WHERE name = ‘张无忌‘");

 

System.out.println(1/0);

 

// 3.在赵敏的账户余额尚增加1000.

st.executeUpdate("UPDATE account SET balance = balance+1000 WHERE name = ‘赵敏‘");

 

//提交事务

conn.commit();

 

 

} catch (Exception e) {

e.printStackTrace();

//回滚事务

conn.rollback();

 

}finally {

DruidUtil.close(conn, st, rs);

}

 

}

}

 

以上是关于JDBC的主要内容,如果未能解决你的问题,请参考以下文章

JDBCJDBC连接mySQL数据库常见错误

JDBCJDBC连接mySQL数据库常见错误

JDBC

关于JDBC小总结

java JDBC编程学习笔记

java JDBC编程学习笔记