- JDBC技术
JDBC:
在 Java 中连接数据库的技术。
在 Java 代码中想操作数据库中的数据。就必须使用到 JDBC 技术。
通过 JDBC 从 Java 代码中向数据库发送执行的 SQL 语句。
JDBC 是什么?
JDBC : Java DataBase Connector 中文:Java 数据库连接技术。
JDBC 是一种技术。一种在 Java 程序中连接数据库的技术。
使用 JDBC 可以实现在 Java 代码中去操作数据库的表中的记录。
JDBC 是一个持久化技术。
持久化:
化:过程。
数据是分为 临时状态 和 持久状态 。
-临时状态:数据保存在内存中。代码在运行时的数据。
-持久状态:数据保存在硬盘中,以文件的方式长期保存的数据。
持久化:数据从 临时状态 与 持久状态 之间进行切换的过程。
- 将 Java 程序中的数据保存到数据库中。
- 将数据库中的数据读取到 Java 程序中。
- JDBC 是一套规范也是标准
JDBC 是指一套规范,约定了 Java 语言连接到不同数据库产品时的标准。
任何数据库产品,想让自己的产品,可以使用 JDBC 连接到 Java 程序,就要按 JDBC 的标准编写数据库的驱动。
数据库的驱动是数据库厂商按 JDBC 标准,由各数据库厂商自己编写的。
JDBC 本身就是一组接口。各大数据库厂商需要根据自己的数据库去实现这一组接口。
- JDBC 中的三个核心接口
Connection 接口:
Connection 是连接对象,负责创建 Java 程序与数据库之间的连接。
Connection 接口的实现类由各数据库厂商提供。称为驱动类。操作数据库之前必先获得驱动类。
Statement 接口:
常用的子接口 PreparedStatement 接口。
PreparedStatement 是操作对象。负责通过 Connection 的连接,向数据库执行 SQL 语句。
ResultSet 接口:
ResultSet 是结果集接口,当我们使PreparedStatement 向数据库执行 Select 命令时,Java 程序需要接收到查询结果。
在Java程序端就是使用 ResultSet 对象来接收查询结果的。
数据库的查询结果就是一个二维表格。
ResultSet 也是一个表格的处理方式。
- 使用 JDBC 操作数据库的基本步骤
使用 JDBC 操作数据库的步骤就三步:
- 打开连接
将 Java 程序与数据库创建连接。 - 操作数据库
通过 Java 程序向数据库发送 SQL 语句执行。
分增删改,和 查询。
查询有返回值,返回值叫结果集。 - 关闭连接
释放资源,一定要执行的部分。finally块中执行。
打开连接:
打开连接,先获取连接数据库的驱动类。
驱动类是数据库厂商提供。是个Jar包。
还要再确定四个参数:
- 驱动类的名称
- 连接数据库的字符串
- 数据库账号
- 密码
//四大参数
String driverClassName="oracle.jdbc.driver.OracleDriver";
String url = "jdbc:oracle:thin:@localhost:1521:XE";
String userName = "scott";
String userPass = "tiger";
//获得连接对象
Connection conn = DriverManager.getConnection(url,userName,userPass);
操作数据库:
- 增删改
//通过PreparedStatement对象来操作数据库
String sql = "insert into users_no5(user_id,user_name) values( SEQ01.nextval , '都醒着')";
PreparedStatement pstat = conn.prepareStatement(sql);
pstat.executeUpdate();//执行增删改语句的方法
- 查询
rs 对象就是一个指向二维表格的引用。rs 对象每次只能指向一行。通过next() 向下移动一行。
通过 getXXX() 方法获取指定列的值。
//通过PreparedStatement对象来操作数据库
String sql = "select * from users_no5";
PreparedStatement pstat = conn.prepareStatement(sql);
ResultSet rs = pstat.executeQuery();//rs就是一个二维表格。
while(rs.next()){//向下移一行。
System.out.println(rs.getInt("user_id"));
System.out.println(rs.getString("user_name"));
System.out.println("----------");
}
关闭连接:
关闭数据库连接对象
//关闭连接
conn.close();
- 预编译的操作接口 PreparedStatement
使用 PreparedStatement 可以提前将要执行的 SQL 语句进行数据库端的预编译。
使用 PreparedStatement 可以提高执行效率。并且安全。防止 SQL 注入。
在哪提前写 SQL 语句:
在创建 pstat 对象时,就已经提前将 SQL 写好了。
String sql = "insert into users_no5(user_id,user_name) values(seq01.nextval,'XXX')" ;
PreparedStatement pstat = conn.prepareStatement(sql);
可以使用 ? 在 SQL 语句中定义变量:
String sql = "insert into users_no5(user_id,user_name) values(seq01.nextval,?)" ;
PreparedStatement pstat = conn.prepareStatement(sql);
可以使用 setXXX() 方法为 ? 赋值:
一定是在 SQL 语句执行之前,先为所有的 ? 号进行赋值。
public abstract void setString(int parameterIndex,String x)
参数:parameterIndex 表示 ? 的索引。从1开始。 x 表示值。
String sql = "insert into users_no5(user_id,user_name) values(seq01.nextval,?)" ;
PreparedStatement pstat = conn.prepareStatement(sql);
pstat.setString(1,"???");
int i = pstat.executeUpdate();
- 经典面试题:
Statement 与 PreparedStatement 之间的区别?
- PreparedStatement 可以使用占位符,是预编译的,批处理比 Statement 效率高。
- Statement 用于执行静态 SQL 语句,在执行时,必须指定一个事先准备好的 SQL 语句。
- PrepareStatement 是预编译的 SQL 语句对象,sql 语句被预编译并保存在对象中。被封装的 sql 语句代表某一类操作,语句中可以包含动态参数“?”,在执行时可以为“?”动态设置参数值。
- 使用 PrepareStatement 对象执行 sql 时,sql 被数据库进行解析和编译,然后被放到命令缓冲区,每当执行同一个 PrepareStatement 对象时,它就会被解析一次,但不会被再次编译。在缓冲区可以发现预编译的命令,并且可以重用。
- PrepareStatement 可以减少编译次数,从而提高数据库性能。
- PreparedStatement 可防止 SQL 注入。
PS:批处理时,以数据量大小为标准选择 Statement 与 PrepareStatement 时的标准:
- 量比较小,二者皆差别不大。
- 量比较多,在 PreparedStatement 预编译空间范围之内,选择PreparedStatement,因为其只预编译一次 sql 语句。
- 量特别大,使用 Statement,因为 PrepareStatement 的预编译空间有限,当数据量特别大时,会发生异常。
- 结果集对象-ResultSet 接口
ResultSet 对象是用来接收查询结果的对象。
数据库的查询结果就是一个二维表格。
rs 对象就是一个指向查询结果每一行的一个指针。
如何获得 RS 对象:
通过 pstat 对象执行 executeQuery() 方法获得一个 RS 对象。
String sql = "select * from users_no5 order by user_id ";
PreparedStatement pstat = conn.prepareStatement(sql);
ResultSet rs = pstat.executeQuery();
- EOF 和 BOF
BOF 表示第一行的上面。
EOF 表示最后一行的下面。
rs 对象就是一个指向查询结果每一行的一个指针。
rs 对象默认指向 BOF。只有调用一次 next() 方法才能指向第一行。
rs 对象调用next()方法,指向了EOF,就表示当前查询结果已经没有了。next() 方法返回 False;
- next() 方法
boolean next() throws SQLException;
向下移动一行。当指向 EOF 时返回 false。
ResultSet rs = pstat.executeQuery();
while(rs.next()){
int userId = rs.getInt("user_id");
String name = rs.getString("user_name");
System.out.println(userId+":"+name);
System.out.println("----------");
}
- getString() 方法
String getString (String columnLabel) throws SQLException;
int getInt (String columnLabel) throws SQLException;
传入参数为列名,用来获取指针指向的这列的数据。
- 封装 JDBC 的操作
封装原则:
封装原则:将变化的 和 不变化的 分开进行封装。
以实现,在需求改时,对变化的部分进行修改时,不会影响到不变化 的部分。
数据库操作步骤的封装:
数据库的操作步骤:3步。
1 打开连接
2 操作数据库
3 关闭连接
----变化 :2 操作数据库 (DAO 数据访问对象)
这部分内容是根据操作的不同,代码不一致。
针对不同的表。针对同一张表的不同的 CRUD。
----不变化 :1 打开连接 和 3 关闭连接 (工具类)
不管对数据库进行什么操作, 都必须先打开连接,最后关闭连接。
- 封装连接对象的工具类(第一版)
在这个工具类中,要封装两个行为。
1 打开连接
3 关闭连接
/**
* 在这个工具类中,要封装二个行为。
* 1 打开连接 getConn()
* 3 关闭连接 closeConn()
*/
public class ConnUtils {
private static Connection conn;
private static final String URL = "jdbc:oracle:thin:@127.0.0.1:1521:XE";//SID数据库实例名
private static final String USER_NAME = "no5";
private static final String USER_PASS = "123456";
private static final String CLASS_NAME = "oracle.jdbc.driver.OracleDriver";
/**
* 打开连接
*
* @return 连接对象
*/
public static Connection getConn() throws SQLException {
if (conn == null || conn.isClosed()) {
conn = DriverManager.getConnection(URL, USER_NAME, USER_PASS);
}
return conn;
}
/**
* 关闭连接
*/
public static void closeConn() throws SQLException {
try {
if (conn != null && !conn.isClosed()) {
conn.close();
}
} finally {
conn = null;
}
}
}
编写实体类,映射数据库表:
数据库表:users_no5
表示的是用户信息。
表中的每一第记录就是一个用户实体。
表结构:
表记录:
用户实体 在 Java 程序中使用什么表示?
//users_no5
public class Users {
// USER_ID NUMBER(7,0)
private Integer userId;
// USER_NAME VARCHAR2(20 BYTE)
private String userName;
......
}
- 编写DAO
DAO:数据访问对象。负责封装操作数据库的代码。
编写 UsersDAO 类:
案例代码如下所示:
/**
* 增加用户信息方法
* @param user
*/
public void insertUser(Users user) throws SQLException {
String sql = "insert into user_testdb (user_id,user_name) values (seq01.nextval,?)";
Connection conn = ConnUtils.getConn();
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1,user.getUserName());
pstmt.executeUpdate();
}
/**
* 查询所有用户信息方法
* @return
* @throws SQLException
*/
public List<Users> selectAll() throws SQLException {
List<Users> users = new ArrayList<>();
Connection conn = ConnUtils.getConn();
String sql = "select * from user_testdb";
PreparedStatement pstmt = conn.prepareStatement(sql);
ResultSet rs = pstmt.executeQuery();
while (rs.next()) {
Users user = new Users(rs.getInt("user_id"),rs.getString("user_name"));
users.add(user);
}
return users;
}
/**
* 删除用户的方法
* @throws SQLException
*/
public void deleteUser(Integer id) throws SQLException {
String sql = "delete from user_testdb where user_id=?";
Connection conn = ConnUtils.getConn();
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setInt(1,id);
pstmt.executeUpdate();
}
/**
* 更新记录
* @param user
* @throws SQLException
*/
public void updateUser(Users user) throws SQLException {
String sql = "update user_testdb set user_name=? where user_id=?";
Connection conn = ConnUtils.getConn();
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1,user.getUserName());
pstmt.setInt(2,user.getUserId());
pstmt.executeUpdate();
}
/**
* 按id来查信息
* @param id 需要查询信息的ID
* @return Users对象
* @throws SQLException
*/
public Users selectById(Integer id) throws SQLException {
String sql = "select * from user_testdb where user_id=?";
Connection conn = ConnUtils.getConn();
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setInt(1,id);
ResultSet rs = pstmt.executeQuery();
if (rs.next()){
Users user = new Users();
user.setUserId(rs.getInt("user_id"));
user.setUserName(rs.getString("user_name"));
return user;
}else{
return null;
}
}
- 使用 JUnit 进行单元测试
Junit 是一个单元测试的软件。
我们编写的类中方法,使用 Junit 可以方便的进行单元测试。
@Test
public void selectById(){
UsersDAO usersDAO = new UsersDAO();
try {
Users users = usersDAO.selectById(1);
System.out.println(users);
} catch (SQLException e) {
e.printStackTrace();
}finally {
try {
ConnUtils.closeConn();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
@Test
public void updateUser(){
Users users = new Users(21,"陆奥克兰");
UsersDAO usersDAO = new UsersDAO();
try {
usersDAO.updateUser(users);
} catch (SQLException e) {
e.printStackTrace();
}finally {
try {
ConnUtils.closeConn();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
@Test
public void deleteUser(){
UsersDAO usersDAO = new UsersDAO();
try {
usersDAO.deleteUser(21);
} catch (SQLException e) {
e.printStackTrace();
}
}
@Test
public void insertUser(){
Users users = new Users();
users.setUserName("阿里");
UsersDAO usersDAO = new UsersDAO();
try {
usersDAO.insertUser(users);
} catch (SQLException e) {
e.printStackTrace();
}finally {
try {
ConnUtils.closeConn();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
@Test
public void selectAll(){
UsersDAO usersDAO = new UsersDAO();
List<Users> users = null;
try {
users = usersDAO.selectAll();
} catch (SQLException e) {
e.printStackTrace();
}
System.out.println(users);
}
- 封装连接对象的工具类(第二版)
第一版在单用户使用时,没有问题。
我们后面一定会学习 web 应用程序开发的。像 taobao,jd 这些是 web 应用程序?
Web 应用程序本身就是一个多用户的应用程序。每一个用户是一个单独的线程。
private static Connection conn;
static 静态,属于类的属性,整个应用程序只有一份。
单用户应用时,只用一个连接对象没关系。
多用户应用时,就变成所有用户使用同一个连接对象。
ThreadLoacl 类:
本地线程容器对象。每一个线程都有一个自己的 ThreadLocal 对象。
这个对象只能保存一个内容。
使用 ThreadLoacl 类修改 ConnUtils 工具类:
public class ConnUtils {
private static final ThreadLocal<Connection> THREAD_LOCAL = new ThreadLocal<>();
private static final String URL = "jdbc:oracle:thin:@127.0.0.1:1521:XE";
private static final String USER_NAME = "no5";
private static final String USER_PASS = "123456";
private static final String CLASS_NAME = "oracle.jdbc.driver.OracleDriver";
/**
* 打开连接
*
* @return 连接对象
*/
public static Connection getConn() throws SQLException {
Connection conn = THREAD_LOCAL.get();
if (conn == null || conn.isClosed()) {
conn = DriverManager.getConnection(URL, USER_NAME, USER_PASS);
THREAD_LOCAL.set(conn);
}
return conn;
}
/**
* 关闭连接
*/
public static void closeConn() throws SQLException {
try {
Connection conn = THREAD_LOCAL.get();
if (conn != null && !conn.isClosed()) {
conn.close();
}
} finally {
THREAD_LOCAL.set(null);
}
}
}