JDBC技术的介绍与案例

2023.03.06 星期一

1. JDBC

在Java中连接数据库的技术。
在Java代码中想操作数据库中的数据。就必须使用到JDBC技术。
通过JDBC从Java代码中向数据库发送执行的SQL语句。
工具类的封装。DAO-Service的分层。
一定要遵守编码规范。

1.1 学习难点

就是一套使用的规范。大家按规范编写就没有什么特别难理解的部分。
难点:
1 分层,理解DAO与Service层的区别。
2 封装工具类。连接对象需要单独封装起来。
2.1 要使用连接池技术
2.2 ThreadLocal类。理解上需要大家想。
3 事务处理,异常处理,连接关闭。这三个内容大家必须清楚在哪处理。

1.2 什么是JDBC

JDBC : Java DataBase Connector 中文:Java数据库连接技术
JDBC是一种技术。一种在Java程序中连接数据库的技术。
使用JDBC可以实现在Java代码中去操作数据库的表中的记录。

JDBC是一个持久化技术。

2. 持久化

化:过程。

数据是分为临时状态和持久状态。
临时状态:数据保存在内存中。代码在运行时的数据。
持久状态:数据保存在硬盘中,以文件的方式长期保存的数据。

持久化:数据从 临时状态 与 持久状态 之间进行切换的过程。
1 将Java程序中的数据保存到数据库中。
2 将数据库中的数据读取到Java程序中。

当我们进行注册操作时,需要将用户注册的新数据保存到数据库中。
当们们进行登录操作时,需要从数据库将用户注册的数据读取到Java程序中。

2. JDBC是一套规范也是标准

JDBC是只一套规范,约定了Java语言连接到不同数据库产品时的标准。
例如:USB接口。
任何数据库产品,想让自己的产品,可以使用JDBC连接到Java程序,就要按JDBC的标准编写数据库的驱动。
数据库的驱动是数据库厂商按JDBC标准,由各数据库厂商自己编写的。
在Java程序中接口表示了一种能力,一种规范,一个标准,一种约定。

JDBC本身就是一组接口。各大数据库厂商需要根据自己的数据库去实现这一组接口。

3. JDBC中的三个核心接口
3.1 Connection接口

Connection是连接对象,负责创建Java程序与数据库之间的连接。
Connection接口的实现类由各数据库厂商提供。称为驱动类。操作数据库之前必先获得驱动类。

3.2 Statement接口

常用的子接口PreparedStatement接口。
PreparedStatement是操作对象。负责通过Connection的连接,向数据库执行SQL语句。

3.3 ResultSet接口

ResultSet是结果集接口,当我们使用PreparedStatement向数据库执行Select命令时,Java程序需要能接收查询结果。
在Java程序端就是使用ResultSet对象来接收查询结果的。
数据库的查询结果就是一个二维表格。
ResultSet也是一个表格的处理方式。

4. 使用JDBC操作数据库的基本步骤

使用JDBC操作数据库的步骤就三步:
1 打开连接
将Java程序与数据库创建连接。
2 操作数据库
通过Java程序向数据库发送SQL语句执行。
分增删改,和 查询。
查询有返回值,返回值叫结果集。
3 关闭连接
释放资源,一定要执行的部分。finally块。

4.1 打开连接

打开连接,先获取连接数据库的驱动类。
驱动类是数据库厂商提供。是个Jar包。
还要再确定四个参数:
1 驱动类的名称
2 连接数据库的字符串
3 数据库账号
4 密码

//四大参数
        String driverClassName="oracle.jdbc.driver.OracleDriver";
        String url = "jdbc:oracle:thin:@localhost:1521:ORCL";
        String userName = "LZK01";
        String userPass = "123456";
        //获得连接对象
        Connection coon = DriverManager.getConnection(url,userName,userPass);
4.2 操作数据库
//通过PreparedStatement对象来操作数据库
String sql = "insert into users_no5(user_id,user_name) values( SEQ01.nextval , '都醒着')";

PreparedStatement pstat = conn.prepareStatement(sql);
pstat.executeUpdate();//执行增删改语句的方法

2 查询
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("----------");
}
4.3 关闭数据库连接对象

//关闭连接

conn.close();
5. 预编译的操作接口PreparedStatement

使用PreparedStatement可以提前将要执行的SQL语句进行数据库端的预编译。
使用PreparedStatement可以提高执行效率。并安全。

5.1 在哪提前写SQL语句

在创建pstat对象时,就已经提前将SQL写好了。

String sql = "insert into users_no5(user_id,user_name) values(seq01.nextval,'XXX')" ;
PreparedStatement pstat = conn.prepareStatement(sql);
5.2 可以使用?在SQL语句中定义变量
String sql = "insert into users_no5(user_id,user_name) values(seq01.nextval,?)" ;
PreparedStatement pstat = conn.prepareStatement(sql);
5.3 可以使用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之间的区别?
1、都是接口,但PrepareStatement 继承了Statement。
2、PrepareStatement 可以使用占位符,用“?”占位,是预编译时期替换为 值。批处理比Statement高。而Statement只能执行静态sql,即:字符串

6. 结果集对象-ResultSet接口

ResultSet对象是用来接收查询结果的对象。
数据库的查询结果就是一个二维表格。

rs对象就是一个指向查询结果每一行的一个指针。

6.1 如何获得RS对象

通过pstat对象执行 executeQuery()方法获得一个RS对象。

String sql = "select * from users_no5 order by user_id ";
PreparedStatement pstat = conn.prepareStatement(sql);
ResultSet rs = pstat.executeQuery();

6.2 EOF和BOF

BOF表示第一行的上面。
EOF表示最后一行的下面。

rs对象就是一个指向查询结果每一行的一个指针。
查询结果能不能没有记录?可以。
所以rs对象默认指向BOF。只有调用一次next()方法才能指向第一行。
如果rs对象调用next()方法,指向了EOF,就表示当前查询结果已经没有了。next()方法返回false;

6.3 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("----------");
}
6.4 getString()方法
String getString(String columnLabel) throws SQLException;
int getInt(String columnLabel) throws SQLException;
7. 封装JDBC的操作
7.1 封装原则

封装原则:将变化的 和 不变化的 分开进行封装。
以实现,在需求改时,对变化的部分进行修改时,不会影响到不变化 的部分。

7.2 数据库操作步骤的封装

数据库的操作步骤:3步。
1 打开连接
2 操作数据库
3 关闭连接

变化 :2 操作数据库 (DAO 数据访问对象)
这部分内容是根据操作的不同,代码不一致。
针对不同的表。针对同一张表的不同的CRUD。

不变化 :1 打开连接 和 3 关闭连接 (工具类)
不管对数据库进行什么操作, 都必须先打开连接,最后关闭连接。

8. 封装连接对象的工具类(第一版)

在这个工具类中,要封装二个行为。
1 打开连接
3 关闭连接

/**
 * 在这个工具类中,要封装二个行为。
 * 1 打开连接   getConn()
 * 3 关闭连接   closeConn()
 */
public class ConnUtils {
    private static Connection conn;
    private static final String URL = "jdbc:oracle:thin:@localhost:1521:ORCL";//SID数据库实例名
    private static final String USER_NAME = "lzk01";
    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;
        }
    }
}
9. 编写实体类,映射数据库表

数据库表:users_no5
表示的是用户信息。
表中的每一第记录就是一个用户实体。
表结构:
在这里插入图片描述
在这里插入图片描述
用户实体 在Java程序中使用什么表示?

//users_no5
public class Users {
    //    USER_ID  NUMBER(7,0)
    private Integer userId;
    //    USER_NAME    VARCHAR2(20 BYTE)
    private String userName;

public static void main(String[] args) {
    Users user = new Users();
    user.setUserId(1);
    user.setUserName("刘晓聪");

    System.out.println(user);
}
10. 编写DAO

DAO:数据访问对象。负责封装操作数据库的代码。

10.1 编写UsersDAO类

案例JdbcTest03:向Users_no5插入一条记录
案例JdbcTest04:查询Users_no5所有记录
需求在UsersDAO类中编写二个方法,封装以上二个案例的内容。

封装的方法原型:
案例JdbcTest03:public void insert(Users user);
案例JdbcTest04:public List selectAll();

public class UsersDAO {
    /**
     * 用来添加数据的方法
     * @param user
     * @throws SQLException
     */
    public void insert (Users user) throws SQLException {
        //通过preparedStatement对象来操作数据库
        String sql = "insert into users_lzk01(users_id,users_name) values(seq01.nextval,?)";
        Connection coon = ConnUtils.getConnection();
        PreparedStatement pstat = coon.prepareStatement(sql);
        pstat.setString(1,user.getUserName());
        pstat.executeUpdate();//执行增删改语句的方法
    }

    /**
     * 删除数据的方法
     * @param userId
     * @throws SQLException
     */
    public void delete(Integer userId) throws SQLException {
        String sql = "delete from users_lzk01 where users_id=?";
        Connection coon = ConnUtils.getConnection();
        PreparedStatement pstat = coon.prepareStatement(sql);
        pstat.setInt(1,userId);
        pstat.executeUpdate();//执行增删改语句的方法
    }

    /**
     * 更新数据的方法
     * @param user
     * @throws SQLException
     */
    public void update(Users user) throws SQLException {
        //通过preparedStatement对象来操作数据库
        String sql = "update users_lzk01 set users_name = ? where users_id = ?";
        Connection coon = ConnUtils.getConnection();
        PreparedStatement pstat = coon.prepareStatement(sql);
        pstat.setString(1,user.getUserName());
        pstat.setInt(2,user.getUserId());
        pstat.executeUpdate();//执行增删改语句的方法
    }

    /**
     * 按ID查询记录的方法
     * @param userId
     * @return
     * @throws SQLException
     */
    public Users selectById(Integer userId) throws SQLException {
        //通过preparedStatement对象来操作数据库
        String sql = "select * from users_lzk01 where users_id = ?";
        Connection coon = ConnUtils.getConnection();
        PreparedStatement pstat = coon.prepareStatement(sql);
        pstat.setInt(1,userId);
        ResultSet rs = pstat.executeQuery();//rs就是一个二维表格
        if(rs.next()){
            Users users = new Users();
            users.setUserId(rs.getInt("users_id"));
            users.setUserName(rs.getString("users_name"));
            return users;
        }else {
            return null;
        }
    }

    /**
     * 用来查找所有记录的方法
     * @return
     * @throws SQLException
     */
    public List<Users> selectAll() throws SQLException {
        List<Users> usersList = new ArrayList<>();
        //通过preparedStatement对象来操作数据库
        String sql = "select * from users_lzk01 order by users_id";
        Connection coon = ConnUtils.getConnection();
        PreparedStatement pstat = coon.prepareStatement(sql);
        ResultSet rs = pstat.executeQuery();//rs就是一个二维表格
        while (rs.next()) {//向下移一行
            Users users = new Users();
            users.setUserId(rs.getInt("users_id"));
            users.setUserName(rs.getString("users_name"));
            usersList.add(users);
        }
        return usersList;
    }
}
11. 使用JUnit进行单元测试

Junit是一个单元测试的软件。
我们编写的类中方法,使用Junit可以方便的进行单元测试。

public class UsersDAOTest {

    //insert方法测试单元
    @Test
    public void insert(){
        //insert 增加一条记录
        Users user = new Users();
        user.setUserName("王五");
        UsersDAO usersDAO = new UsersDAO();
        try {
            usersDAO.insert(user);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            try {
                ConnUtils.closeConn();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }

    //delete测试单元
    @Test
    public void delete(){
        UsersDAO usersDAO = new UsersDAO();
        try {
            usersDAO.delete(27);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            try {
                ConnUtils.closeConn();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }

    //update测试单元
    @Test
    public void update(){
        Users user = new Users();
        UsersDAO usersDAO = new UsersDAO();
        user.setUserId(25);
        user.setUserName("王五");
        try {
            usersDAO.update(user);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            try {
                ConnUtils.closeConn();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }

    //selectById测试单元
    @Test
    public void selectById(){
        UsersDAO usersDAO = new UsersDAO();
        try {
            Users users = usersDAO.selectById(1);
            System.out.println("所查询的信息为:"+users);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            try {
                ConnUtils.closeConn();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }

    }

    //selectAll测试单元
    @Test
    public void selectAll(){
        UsersDAO usersDAO = new UsersDAO();
        List<Users> usersList = null;
        try {
            usersList = usersDAO.selectAll();
            System.out.println(usersList);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            try {
                ConnUtils.closeConn();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }

}
12. 封装连接对象的工具类(第二版)

第一版在单用户使用时,没有问题。

我们后面一定会学习web应用程序开发的。像taobao,jd这些是web应用程序?
Web应用程序本身就是一个多用户的应用程序。每一个用户是一个单独的线程。

private static Connection conn;

static 静态,属于类的属性,整个应用程序只有一份。
单用户应用时,只用一个连接对象没关系。
多用户应用时,就变成所有用户使用同一个连接对象。

要求:操作数据库一共有3个步骤。第3步是什么?关闭连接。

12.1 ThreadLoacl类

本地线程容器对象。每一个线程都有一个自己的ThreadLocal对象。
这个对象只能保存一个内容。

12.2 使用ThreadLoacl类修改ConnUtils工具类
public class ConnUtils {

    private static final ThreadLocal<Connection>  THREAD_LOCAL= new ThreadLocal<>();
    //四大参数
    private static final String CLASS_NAME="oracle.jdbc.driver.OracleDriver";
    private static final String URL = "jdbc:oracle:thin:@localhost:1521:ORCL";
    private static final String USER_NAME = "LZK01";
    private static final String USER_PASS = "123456";
    /**
     * 打开连接
     * @return
     */
    public static Connection getConnection() 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);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值