软件设计之JDBC(3)

2024-09-06 11:04
文章标签 jdbc 软件设计

本文主要是介绍软件设计之JDBC(3),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

软件设计之JDBC(3)

此篇应在MySQL之后进行学习:
路线图推荐:
【Java学习路线-极速版】【Java架构师技术图谱】
尚硅谷2024最新JDBC教程 | jdbc基础到高级一套通关!

资料可以去尚硅谷官网免费领取

学习内容:

  1. JDBC优化及工具类的封装
  2. ThreadLocal概述
  3. DAO概念及搭建
  4. 事务的概述

1、JDBC优化及工具类的封装

JDBC过程中,部分代码存在冗余问题:创建连接池、获取连接、连接的回收
因此将创建连接池、获取连接、连接的回收这三步封装为一个类,提供静态代码块初始连接池对象,提供获取连接、回收连接的静态方法。

package com.atguigu.senior.util;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.atguigu.advanced.pool.DruidTest;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
/** 1 维护一个连接池对象* 2 对外提供在连接池中获取连接的方法* 3 对外提供回收连接的方法* 注意:工具类仅对外提供共性的功能代码,所以方法均为静态方法*  */
public class JDBCUtil {//创建连接池引用private static DataSource dataSource;//在项目启动时,即创建连接池对象,赋值给dataSourcestatic {try {Properties properties = new Properties();InputStream inputStream = JDBCUtil.class.getClassLoader().getResourceAsStream("db.properties");properties.load(inputStream);dataSource = DruidDataSourceFactory.createDataSource(properties);} catch (Exception e) {throw new RuntimeException(e);}}//对外提供在连接池中获取连接的方法public static Connection getConnection(){try {return dataSource.getConnection();} catch (SQLException e) {throw new RuntimeException(e);}}//对外提供回收连接的方法public static void  release(Connection connection){try {connection.close();} catch (SQLException e) {throw new RuntimeException(e);}}
}

2、ThreadLocal概述

针对同一用户线程多次操作获取多个连接,造成连接资源的浪费,利用ThreadLocal进行优化

在这里插入图片描述

/** 1 维护一个连接池对象,同时维护了一个线程绑定变量的ThreadLocal对象* 2 对外提供在ThreadLocal中获取连接的方法* 3 对外提供回收连接的方法,回收过程中,将要回收的连接从ThreadLocal中移除* 注意:工具类仅对外提供共性的功能代码,所以方法均为静态方法*  */
public class JDBCUtilV2 {//创建连接池引用private static DataSource dataSource;private static  ThreadLocal<Connection> threadLocal = new ThreadLocal<>();//在项目启动时,即创建连接池对象,赋值给dataSourcestatic {try {Properties properties = new Properties();InputStream inputStream = JDBCUtil.class.getClassLoader().getResourceAsStream("db.properties");properties.load(inputStream);dataSource = DruidDataSourceFactory.createDataSource(properties);} catch (Exception e) {throw new RuntimeException(e);}}//对外提供在连接池中获取连接的方法public static Connection getConnection(){try {//在ThreadLocal中获取ConnectionConnection connection = threadLocal.get();//ThreadLocal里没有Connection,也就是第一次获取if (connection == null) {//在连接池中获取一个连接,存储在ThreadLocalconnection = dataSource.getConnection();threadLocal.set(connection);}return  connection;} catch (SQLException e) {throw new RuntimeException(e);}}//对外提供回收连接的方法public static void release(){try {Connection connection = threadLocal.get();if (connection!=null){//从threadlocal中移除当前已经存储的Connection对象threadLocal.remove();//将Connection对象归还给连接池connection.close();}} catch (SQLException e) {throw new RuntimeException(e);}}
}

3、DAO概念及搭建

DAO:Data Access Object 数据访问对象
一张表对应一个实体类,一张表的操作对应一个DAO对象
在Java操作数据库时,将对同一张表的增删改查操作统一维护起来,维护的这个类就是DAO层
DAO层只关注对数据库的操作,供业务层的Service调用

接口设计

/*
* EmployeeDao这个类对应的是t_emp这张表的增删改查操作
* */
public interface EmployeeDao {/** 数据库对应的查询所有操作* @return 表中所有的数据* */List<Employee> selectAll();/** 数据库对应的根据empId查询单个员工数据操作* @param empId 主键列* @return 一个员工对象(一行数据)* */Employee selectByEmpId(Integer empId);/** 数据库对应的新增一条员工数据* @param employee ORM思想中的一个员工对象* @return 受影响行数* */int insert(Employee employee);/** 数据库对应的修改一条员工数据* @param employee ORM思想中的一个员工对象* @return 受影响行数* */int update(Employee employee);/** 数据库对应的根据empId删除一条员工数据* @param empId 主键列* @return 受影响行数* */int delete(Integer empId);
}

BaseDAO

> 这里是引用

BaseDAO方法搭建

/*
* 将共性的数据库操作代码封装在BaseDAO里
* */
public class BaseDAO {/** 通过的增删改的方法* @parma sql调用者要执行的SQL语句* @parma SQL语句中的占位符要赋值的参数* @return 受影响行数* */public int executeUpdate(String sql,Object... params) throws Exception{//通过JDBCUtilV2获取数据库连接Connection connection = JDBCUtilV2.getConnection();//预编译SQL语句PreparedStatement preparedStatement = connection.prepareStatement(sql);//为占位符赋值,执行SQL,接收返回结果if(params!=null && params.length>0){for (int i = 0; i < params.length; i++) {//占位符是从1开始,数组是从0开始preparedStatement.setObject(i+1,params[i]);}}int row = preparedStatement.executeUpdate();//释放资源preparedStatement.close();JDBCUtilV2.release();//返回结果return row;}/** 通用的查询:多行多列、单行多列、单行单列* 多行多列 List<Employee>* 单行多列 Employee* 单行单列 封装的是一个结果 Double、Integer...* 封装过程:*       返回的类型:泛型:类型不确定,调用者知道,调用时将此次查询结果类型告知BaseDAO*       返回的结果:通用 List 可以存储多个结果,也可以存储一个结果 get(0)*       结果的封装:反射,要求调用者告知BaseDAO要封装对象的类对象 Class* */public <T> List<T> executeQuery(Class<T> clazz,String sql,Object... params)throws Exception{//通过JDBCUtilV2获取数据库连接Connection connection = JDBCUtilV2.getConnection();//预编译SQL语句PreparedStatement preparedStatement = connection.prepareStatement(sql);//为占位符赋值,执行SQL,接收返回结果if(params!=null && params.length>0){for (int i = 0; i < params.length; i++) {//占位符是从1开始,数组是从0开始preparedStatement.setObject(i+1,params[i]);}}//执行SQL,并接收返回的结果集ResultSet resultSet = preparedStatement.executeQuery();//获取结果集中的元数据对象//其中包含了列的数量、每个列的名称ResultSetMetaData metaData = resultSet.getMetaData();int columCount = metaData.getColumnCount();List<T> list = new ArrayList<>();//处理结果while (resultSet.next()){// T t = clazz.newInstance();已弃用//getDeclaredConstructor()方法会根据他的参数对该类的构造函数进行搜索并返回对应的构造函数,没有参数就返回该类的无参构造函数,然后再通过newInstance进行实例化。T t = clazz.getDeclaredConstructor().newInstance();for (int i = 1; i <=columCount; i++) {//通过下标获取列的值Object value = resultSet.getObject(i);//获取到的列的value值,这个值就是t这个对象中的某一个属性//获取当前拿到的列的名字 = 对象的属性名String fieldName = metaData.getColumnLabel(i);//通过类对象和fieldName获取要封装的对象的属性Field field = clazz.getDeclaredField(fieldName);//突破封装的privatefield.setAccessible(true);field.set(t,value);}list.add(t);}resultSet.close();preparedStatement.close();JDBCUtilV2.release();return list;}/** 通用查询:在上面查询的集合结果中获取第一个结果*/public <T> T executeQueryBean(Class<T> clazz,String sql,Object... params)throws Exception{List<T> list = this.executeQuery(clazz, sql, params);if (list==null || list.size()==0){return null;}return list.get(0);}
}

实现接口

public class EmployeeDaoImpl extends BaseDAO implements EmployeeDao {@Overridepublic List<Employee> selectAll() {try {String sql = "SELECT emp_id empId ,emp_name empName,emp_salary empSalary,emp_age empAge FROM t_emp";return executeQuery(Employee.class,sql,null);} catch (Exception e) {throw new RuntimeException(e);}}@Overridepublic Employee selectByEmpId(Integer empId) {try {String sql = "SELECT emp_id empId ,emp_name empName,emp_salary empSalary,emp_age empAge FROM t_emp WHERE emp_id = ?";return executeQueryBean(Employee.class,sql,empId);} catch (Exception e) {throw new RuntimeException(e);}}@Overridepublic int insert(Employee employee) {try {String sql = "INSERT INTO t_emp(emp_name,emp_salary,emp_age) VALUES(?,?,?)";return executeUpdate(sql,employee.getEmpName(),employee.getEmpSalary(),employee.getEmpAge());} catch (Exception e) {throw new RuntimeException(e);}}@Overridepublic int update(Employee employee) {//类似insert}@Overridepublic int delete(Integer empId) {//类似insert}
}

4、事务的概述

在这里插入图片描述

事务的特性

在这里插入图片描述

JDBC中事务实现

优化1:在release函数中,需要添加一条代码connection.setAutoCommit(true);
原因1:如果开启了事务的手动提交,操作完毕后,归还给连接池之前,要将事务的自动提交改为true
优化2:在BaseDAO中的增删改查代码里需要在释放连接时进行判断,如果是自动提交事务。则关闭连接,否则不执行操作
原因2:关闭连接就代表操作已经完成了,但是手动提交事务,还会存在回滚操作,得给回滚操作预留执行代码
优化3:设计BankDao接口,继承BaseDAO和实现BankDao接口设计BankDaoImpl,主要存放加钱、减钱函数操作

public  void testTransaction(){//接口引用指向实现类对象BankDao bankDao = new BankDaoImpl();Connection connection = null;try {//获取连接connection = JDBCUtilV2.getConnection();connection.setAutoCommit(false);//操作减钱bankDao.subMoney(1,100);int i = 10/0;//操作加钱bankDao.addMoney(2,100);//前置的多次dao操作,没有异常,提交事务connection.commit();} catch (Exception e) {try {connection.rollback();} catch (Exception ex) {throw new RuntimeException(ex);}throw new RuntimeException(e);}finally {//关闭连接JDBCUtilV2.release();}}

这篇关于软件设计之JDBC(3)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1141822

相关文章

滚雪球学Java(87):Java事务处理:JDBC的ACID属性与实战技巧!真有两下子!

咦咦咦,各位小可爱,我是你们的好伙伴——bug菌,今天又来给大家普及Java SE啦,别躲起来啊,听我讲干货还不快点赞,赞多了我就有动力讲得更嗨啦!所以呀,养成先点赞后阅读的好习惯,别被干货淹没了哦~ 🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,助你一臂之力,带你早日登顶🚀,欢迎大家关注&&收藏!持续更新中,up!up!up!! 环境说明:Windows 10

Hibernate框架中,使用JDBC语法

/*** 调用存储过程* * @param PRONAME* @return*/public CallableStatement citePro(final String PRONAME){Session session = getCurrentSession();CallableStatement pro = session.doReturningWork(new ReturningWork<C

读软件设计的要素04概念的关系

1. 概念的关系 1.1. 概念是独立的,彼此间无须相互依赖 1.1.1. 一个概念是应该独立地被理解、设计和实现的 1.1.2. 独立性是概念的简单性和可重用性的关键 1.2. 软件存在依赖性 1.2.1. 不是说一个概念需要依赖另一个概念才能正确运行 1.2.2. 只有当一个概念存在时,包含另一个概念才有意义 1.3. 概念依赖关系图简要概括了软件的概念和概念存在的理

jdbc连接数据库使用sid和service_name的区别 ?

问题描述: ORA-12505, TNS:listener does not currently know of SID given in connect descriptor The Connection descriptor used by the client was: 10.12.162.84:1521:xxxx  oracle数据的tnsnames.ora中配置的是:SERVICE

Java项目中,配置打印 JDBC 日志的几种方法

在 IDEA 项目中,如果你想打印 JDBC 日志,可以通过配置日志框架(如 Logback 或 Log4j)来实现。Spring Boot 使用的默认日志框架是 Logback,你可以通过在 application.yml 文件中配置日志级别来打印 JDBC 日志。 方法 1: 使用 application.yml 配置 JDBC 日志 logging:level:# 显示 SQL 语句co

基于shard-jdbc中间件,实现数据分库分表

一、水平分割 1、水平分库 1)、概念: 以字段为依据,按照一定策略,将一个库中的数据拆分到多个库中。 2)、结果 每个库的结构都一样;数据都不一样; 所有库的并集是全量数据; 2、水平分表 1)、概念 以字段为依据,按照一定策略,将一个表中的数据拆分到多个表中。 2)、结果 每个表的结构都一样;数据都不一样; 所有表的并集是全量数据; 二、Shard-jdbc 中间件 1、架构图 2、特点

基于Shard-Jdbc分库分表,数据库扩容方案

一、数据库扩容 1、业务场景 互联网项目中有很多“数据量大,业务复杂度高,需要分库分表”的业务场景。 这样分层的架构 (1)上层是业务层biz,实现业务逻辑封装; (2)中间是服务层service,封装数据访问; (3)下层是数据层db,存储业务数据; 2、扩容场景和问题 当数据量持续新增,面临着这样一些需求,两台数据库无法容纳,需要数据库扩容,这里选择2台—扩容到3台的模式,如下图

Java笔试面试题AI答之JDBC(3)

文章目录 13. 编写JDBC连Oracle的程序?14. 简述JDBC的主要组件有哪些 ?15. JDBC中如何防止SQL注入攻击?1. 使用预处理语句(PreparedStatement)2. 避免在SQL查询中直接拼接用户输入的数据总结 16. JDBC的脏读是什么?哪种数据库隔离级别能防止脏读?脏读(Dirty Read)哪种数据库隔离级别能防止脏读? 17. 简述JDBC ex

JavaBug系列- Failed to load driver class com.mysql.cj.jdbc.Driver in either of HikariConfig class load

JavaBug系列之Mysql驱动问题 Java医生一、关于错误信息二、如何解决问题 Java医生 本系列记录常见Bug,以及诊断过程和原因 Java/一对一零基础辅导/企业项目一对一辅导/日常Bug解决/代码讲解/毕业设计等 V:study_51ctofx 一、关于错误信息 APPLICATION FAILED TO START Description: Fai

读软件设计的要素03概念的组合

1. 概念的组合 1.1. 概念不像程序那样,可以用较大的包含较小的 1.1.1. 每个概念对用户来说都是平等的,软件或系统就是一组串联运行的概念组合 1.2. 概念是通过操作来同步组合的 1.2.1. 同步并不增加新的概念操作,但会限制已有的操作,从而消除一些独立概念可能会出现的操作序列 1.3. 在自由组合中,概念彼此独立,仅受一些记录的约束,这些约束是为了确保概念对事物观点的一