当前位置: 首页 > news >正文

Java数据库部分(MySQL+JDBC)(二、JDBC超详细学习笔记)

文章目录

  • 1 JDBC(Java Database Connectivity)
    • 1.1 什么是 JDBC?
    • 1.2 JDBC 核心思想
  • 2 JDBC开发步骤【重点】
    • 2.0 环境准备
    • 2.1 注册数据库驱动
    • 2.2 获取数据库的连接
    • 2.3 获取数据库操作对象Statement
    • 2.4 通过Statement对象执行SQL语句
    • 2.5 处理返回结果
    • 2.6 释放资源
  • 3 ResultSet结果集
    • 3.1 查询案例
  • 四、综合案例【登录】
    • 4.1 登录分析
    • 4.2 登录案例实现
    • 4.3 登录案例分析-SQL注入问题
    • 4.4 登录案例更新
    • 4.5 PreparedStatement
  • 五、ORM映射
        • 5.1 思想
    • 5.2 实体类的规范
    • 5.3 代码实现
  • 6 封装工具类
    • 6.1 代码实现
    • 6.2 工具类测试
  • 7 DAO (Data Access Object)
    • 7.1 开发流程
      • 7.1.1 EmpDao接口
      • 7.1.2 实体类
      • 7.1.3 EmpDao实现类
      • 7.1.4 测试类
  • 8 Service 业务 【掌握】
    • 8.1 什么是业务
    • 8.2 转账业务开发
  • 9 事务
    • 9.1 转账业务实现
    • 9.2 解决方案1:传递 Connection
    • 9.3 解决方案2:ThreadLocal
    • 9.4 使用ThreadLocal更新JDBC工具类
    • 9.5 事务封装
    • 9.6 最终的转账业务
  • 10 三层架构【理解】
  • 11 单元测试
    • 11.1 单元测试
    • 11.2 实际应用
  • 12 连接池【理解掌握】
    • 12.1 Druid连接池
    • 12.2 db.properties
    • 12.3 最终版JDBC工具类
    • 12.4 连接池测试
  • 14 DaoUtils工具类【理解】
    • 13.1 工具类实现
    • 13.2 DaoUtils工具类使用
  • 14 DBUtils工具类
    • 14.1 DBUtils简介
        • 14.2 DbUtils核心API
    • 14.3 DbUtils的使用步骤
    • 14.4 DBUtils使用
    • 14.5 字段与属性名不一致
  • 15 Lombok插件

1 JDBC(Java Database Connectivity)

1.1 什么是 JDBC?

JDBC(Java Database Connectivity) Java 连接数据库的规范(标准),可以使用 Java 语言连接数据库完成 CRUD 操作

1.2 JDBC 核心思想

Java 中定义了访问数据库的接口,可以为多种关系型数据库提供统一的访问方式。由数据库厂商提供驱动实现类(Driver 数据库驱动)。

JDBC核心思想
在这里插入图片描述

2 JDBC开发步骤【重点】

/*** JDBC的开发步骤:*    0、准备工作*         a、准备数据库和数据表*         b、导入jar包* 			 	    1、在工程下新建一个lib的文件,将驱动jar包放入到文件夹中 *  			  	2、右击jar包---> add as library*                  注意:类路径(classpath) 就是java代码生成.class文件的路径*    1、注册驱动*    2、获取数据库连接对象Connection*    3、获取数据库操作对象*    4、通过数据库操作对象执行SQL语句*    5、处理结果*    6、释放资源*/

2.0 环境准备

环境准备

  • a、将数据库连接驱动包赋值到当前工程的lib文件夹下
  • b、将jar包添加到类路径(class文件生成的位置)中。右击jar包 —> add as library

2.1 注册数据库驱动

//1、注册数据库驱动
//实现方式一:不推荐使用
//Driver driver = new Driver();
//DriverManager.registerDriver(driver);
//实现方式二:推荐使用
Class.forName("com.mysql.jdbc.Driver");//主动触发类加载,执行静态代码块,进而注册驱动

2.2 获取数据库的连接

协议://地址:端口号

  • mysql 5.7版本 jdbc:mysql://localhost:3306/数据库名
    • mysql 8.x版本 需要加上指定时区 jdbc:mysql://localhost:3306/数据库名?serverTimezone=?serverTimezone = UTC (北京时间)
    • jdbc:mysql://localhost:3306/数据库名?serverTimezone=?serverTimezone = Asia/shanghai (j精确到时分秒时间)
  • 本机数据库localhost:3306可以省略不写,即 jdbc:mysql:///数据库名
 // 2、获取数据库连接对象Connection
/*** 网络编程三要素*      协议+地址+端口*      jdbc:mysql://localhost:3306/数据库名?serverTimezone=UTC*/      
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/java2303", "root", "123456");

2.3 获取数据库操作对象Statement

// 3、获取数据库操作对象
Statement statement = conn.createStatement();

2.4 通过Statement对象执行SQL语句

// 4、通过数据库操作对象执行SQL语句
int count = statement.executeUpdate("insert into emp (empno,ename,job,mgr,hiredate,sal,comm,deptno) values (6666,'张三','java',7369,'2021-05-01',30000,200,30)");

2.5 处理返回结果

// 5、处理结果
if(count > 0){System.out.println("插入成功!!");
}else{System.out.println("插入失败!!");
}

2.6 释放资源

//6、释放资源
statement.close();
conn.close();

3 ResultSet结果集

//5、处理结果集
/*** ResultSet提供了一组方法*          next(); 判断是否有下一行数据,如果有返回true,并将游标移动到下一行*          getXXX("字段名");    XXX表示字段在java中的对应的类型*          getXXX(列的下标);    XXX表示字段在java中的对应的类型*/

3.1 查询案例

查询emp表的数据

while(rs.next()){int empno = rs.getInt("empno");//int empno = rs.getInt(1);System.out.println(empno);String ename = rs.getString("ename");System.out.println(ename);String job = rs.getString("job");System.out.println(job);Date hiredate = rs.getDate("hiredate");System.out.println(hiredate);double sal = rs.getDouble("sal");System.out.println(sal);
}

四、综合案例【登录】

4.1 登录分析

/*** 登录的思路:*      1、根据用户输入的用户名和密码到数据库中查询*          select * from tb_user where username = "你输入的用户名" and password = "你输入的密码"*          select * from tb_user where username = admin and password = 123*          如果有结果:登录成功*          如果没有结果:登录失败*/

4.2 登录案例实现

public class JDBCDemo05 {    public static void main(String[] args) throws Exception {Scanner sc = new Scanner(System.in);System.out.println("请输入用户名");String username = sc.nextLine();System.out.println("请输入密码");String password = sc.nextLine();Class.forName("com.mysql.jdbc.Driver");Connection conn = DriverManager.getConnection("jdbc:mysql:///java2303", "root", "123456");Statement statement = conn.createStatement();String sql = " select * from tb_user where username = '" + username + "' and password = '" + password +"'";System.out.println(sql);ResultSet rs = statement.executeQuery(sql);if(rs.next()){//登录成功System.out.println("登录成功");}else{//登录失败System.out.println("登录失败");}rs.close();statement.close();conn.close();}
}

4.3 登录案例分析-SQL注入问题

以上代码会出现SQL注入问题

  • 用户输入的数据中有 SQL 关键字或语法并且参与了 SQL 语句的编译,导致 SQL 语句编译后的条件含义为 true,一直得到正确的结果。这种现象称为 SQL 注入。
  • 字符串的拼接操作也是非常的不方便

4.4 登录案例更新

public class JDBCDemo06 {public static void main(String[] args) throws Exception {Scanner sc = new Scanner(System.in);System.out.println("请输入用户名");String username = sc.nextLine();System.out.println("请输入密码");String password = sc.nextLine();Class.forName("com.mysql.jdbc.Driver");Connection conn = DriverManager.getConnection("jdbc:mysql:///java2303", "root", "123456");//3、获取数据库操作对象PreparedStatement    预加载SQL语句PreparedStatement ps =conn.prepareStatement("select * from tb_user where username = ? and password = ?");//3.1 设置占位符的值/***  PreparedStatement对象中提供了一组方法*      setXXX(index从1开始,要传入的值)    XXX表示对应的java类型*/ps.setString(1,username);ps.setString(2,password);System.out.println(ps);//4、执行SQL语句ResultSet rs = ps.executeQuery();if(rs.next()){//登录成功System.out.println("登录成功");}else{//登录失败System.out.println("登录失败");}rs.close();ps.close();conn.close();}
}

4.5 PreparedStatement

PreparedStatement 继承了 Statement 接口,执行 SQL 语句的方法无异。

作用:

  • 预编译SQL 语句,效率高。
  • 安全,避免SQL注入

五、ORM映射

5.1 思想

ORM (Object Relational Mapping)

将数据库中的表的数据,一行一行的映射到Java对象中

ORM思想
请添加图片描述

5.2 实体类的规范

JavaBean设计规范
1、类名与表名一致,属性名和字段名一致

2、私有化属性对外提供set、get方法

3、提供有参和无参构造

4、基本数据类型要使用包装类(默认值)

5、在需要的时候实现序列化接口

5.3 代码实现

查询

public class JDBCDemo04 {public static void main(String[] args) throws Exception {//1、注册驱动Class.forName("com.mysql.jdbc.Driver");//2、获取数据库连接对象Connection conn = DriverManager.getConnection("jdbc:mysql:///java2303", "root", "123456");//3、获取数据库操作对象Statement statement = conn.createStatement();//4、执行SQL语句ResultSet rs = statement.executeQuery("select * from emp");//5、处理结果集//定义List集合保存所有的emp对象List<Emp> empList = new ArrayList<>();while(rs.next()){int empno = rs.getInt("empno");String ename = rs.getString("ename");String job = rs.getString("job");int mgr = rs.getInt("mgr");Date hiredate = rs.getDate("hiredate");double sal = rs.getDouble("sal");double comm = rs.getDouble("comm");int deptno = rs.getInt("deptno");//将查询的结果存放到Emp对象中Emp emp = new Emp(empno,ename,job,mgr,hiredate,sal,comm,deptno);//将Emp对象添加到List中empList.add(emp);}//遍历集合中所有元素empList.forEach(System.out::println);
//        for(Emp emp : empList){
//            System.out.println(emp);
//        }//6、释放资源rs.close();statement.close();conn.close();}
}

6 封装工具类

1、注册数据库驱动(静态代码块)

2、获取数据库连接 (方法:getConnection)

3、释放资源 (方法:closeAll(Connection , Statement , ResultSet ))

4、将配置信息放到properties配置文件中,减少硬编码

6.1 代码实现

工具类代码

/**
* 数据库工具类*/
public class JDBCUtils {/*** 1、注册驱动(只需要注册一次)*      可以将注册驱动的代码写到静态代码块中* 2、获取数据库连接*      提供静态一个方法,用于返回数据库连接对象* 3、关闭资源*      提供静态一个方法,用于释放资源* 4、硬编码问题(.xml  .properties)*      将数据源信息保存到配置文件中,然后在代码中进行读取*      properties文件的格式:key和value都是String类型*              key=value*/private static String driver;private static String url;private static String username;private static String password;static{try {//读取配置文件Properties properties = new Properties();//通过类对象读取当前类路径下的资源InputStream in = JDBCUtils.class.getClassLoader().getResourceAsStream("db.properties");//将配置文件中的信息读取到Properties集合中properties.load(in);//从集合中取出数据driver = properties.getProperty("driver123");url = properties.getProperty("url");username = properties.getProperty("username");password = properties.getProperty("password");//注册驱动Class.forName(driver);} catch (Exception e) {e.printStackTrace();}}public static Connection getConnection(){Connection conn = null;try {conn = DriverManager.getConnection(url,username,password);} catch (SQLException throwables) {throwables.printStackTrace();}return conn;}public static void closeAll(Connection conn, Statement statement, ResultSet rs){try {if(rs!=null)rs.close();if(statement != null)statement.close();if(conn != null)conn.close();} catch (SQLException throwables) {throwables.printStackTrace();}}
}

编写配置文件properties (集合工具类-自己补充)

  • 在src更目录下创建xx.properties的new file
  • 集合键值对 driver = com.mysql.jdbc.Driver url = username = password =
  • 在静态代码块读取配置文件
    1. 创建properties对象

配置文件 db.properties

driver123=com.mysql.jdbc.Driver
url=jdbc:mysql:///java2303
username=root
password=123456

6.2 工具类测试

public class JDBCDemo01 {public static void main(String[] args) throws SQLException {//2、获取数据库连接Connection conn = JDBCUtils.getConnection();//3、获取数据库操作对象PreparedStatement ps = conn.prepareStatement("select * from emp");//4、执行SQL语句ResultSet rs = ps.executeQuery();//5、处理结果集while(rs.next()){int empno = rs.getInt("empno");String ename = rs.getString("ename");String job = rs.getString("job");int mgr = rs.getInt("mgr");Date hiredate = rs.getDate("hiredate");double sal = rs.getDouble("sal");double comm = rs.getDouble("comm");int deptno = rs.getInt("deptno");System.out.println(ename);}//6、释放资源JDBCUtils.closeAll(conn,ps,rs);}
}

7 DAO (Data Access Object)

DAO 实现了业务逻辑与数据库访问相分离。

  • 对同一张表的所有操作封装在XxxDaoImpl对象中。
  • 根据增删改查的不同功能实现具体的方法(insert、update、delete、select、selectAll)。

7.1 开发流程

7.1.1 EmpDao接口

public interface EmpDao {//增加员工int insertEmp(Emp emp) throws SQLException;//删除员工int deleteEmp(int empno);//修改员工int updateEmp(Emp emp) throws SQLException;//查询所有员工List<Emp> selectAll() throws SQLException;//查询单个员工Emp selectOne(int empno);
}

7.1.2 实体类

com.qf.pojo包下创建实体类

public class Emp {private Integer empno;private String ename;private String job;private Integer mgr;private Date hiredate;private Double sal;private Double comm;private Integer deptno;//省略get、set、构造方法
}

7.1.3 EmpDao实现类

public class EmpDaoImpl implements EmpDao {@Overridepublic int insertEmp(Emp emp) throws SQLException {//2、获取数据库连接对象Connection conn = JDBCUtils.getConnection();//3、获取数据库操作对象PreparedStatement ps = conn.prepareStatement("insert into emp values(?,?,?,?,?,?,?,?)");//3.1设置占位符的值ps.setInt(1,emp.getEmpno());ps.setString(2,emp.getEname());ps.setString(3,emp.getJob());ps.setInt(4,emp.getMgr());//将util.date 转换成 sql.dateps.setDate(5,new Date(emp.getHireadate().getTime()));ps.setDouble(6,emp.getSal());ps.setDouble(7,emp.getComm());ps.setInt(8,emp.getDeptno());//4、执行SQL语句int count = ps.executeUpdate();//5、处理结果//6、释放资源JDBCUtils.closeAll(conn,ps,null);return count;}@Overridepublic int deleteEmp(int empno) {return 0;}@Overridepublic int updateEmp(Emp emp) throws SQLException {//2、获取数据库连接对象Connection conn = JDBCUtils.getConnection();//3、获取数据库操作对象PreparedStatement ps = conn.prepareStatement("update emp set ename=?,job=?,mgr=?,hiredate=?,sal=?,comm=?,deptno=? where empno = ?");//3.1设置占位符的值ps.setString(1,emp.getEname());ps.setString(2,emp.getJob());ps.setInt(3,emp.getMgr());//将util.date 转换成 sql.dateps.setDate(4,new Date(emp.getHireadate().getTime()));ps.setDouble(5,emp.getSal());ps.setDouble(6,emp.getComm());ps.setInt(7,emp.getDeptno());ps.setInt(8,emp.getEmpno());//4、执行SQL语句int count = ps.executeUpdate();//5、处理结果//6、释放资源JDBCUtils.closeAll(conn,ps,null);return count;}@Overridepublic List<Emp> selectAll() throws SQLException {//2、获取数据库连接对象Connection conn = JDBCUtils.getConnection();//3、获取数据库操作对象PreparedStatement ps = conn.prepareStatement("select * from emp");//4、执行SQL语句ResultSet rs = ps.executeQuery();//5、处理结果集List<Emp> empList = new ArrayList<>();while(rs.next()){int empno = rs.getInt("empno");String ename = rs.getString("ename");String job = rs.getString("job");int mgr = rs.getInt("mgr");Date hiredate = rs.getDate("hiredate");double sal = rs.getDouble("sal");double comm = rs.getDouble("comm");int deptno = rs.getInt("deptno");//将查询的结果存放到Emp对象中Emp emp = new Emp(empno,ename,job,mgr,hiredate,sal,comm,deptno);//将Emp对象添加到List中empList.add(emp);}//6、释放资源JDBCUtils.closeAll(conn,ps,rs);return empList;}@Overridepublic Emp selectOne(int empno) {return null;}
}

7.1.4 测试类

public class TestEmpDao {public static void main(String[] args) throws Exception {//测试查询所有
//        EmpDao empDao = new EmpDaoImpl();
//        List<Emp> empList = empDao.selectAll();
//        System.out.println(empList);//测试增加
//        EmpDao empDao = new EmpDaoImpl();
//        Emp emp = new Emp(9000,"韩梅梅","mysql",7369,new Date(),4000d,200d,30);
//        System.out.println(empDao.insertEmp(emp));//测试修改EmpDao empDao = new EmpDaoImpl();Emp emp = new Emp(9000,"李雷","java",7369,new Date(),40000d,200d,30);System.out.println(empDao.updateEmp(emp));}
}

8 Service 业务 【掌握】

8.1 什么是业务

代表用户完成的一个业务功能,可以由一个或多个DAO的调用组成。(软件所提供的一个功能都叫业务)

8.2 转账业务开发

转账业务分析
在这里插入图片描述
public class AccountServiceImpl implements AccountService {AccountDao accountDao = new AccountDaoImpl();@Overridepublic String zhuanZhang(String fromName, String password, String toName, double money) {try {//1、验证我方用户密码Account account = accountDao.selectAccount(fromName);if(account == null){return "用户名不存在";}if(!account.getPassword().equals(password)){return "用户密码不正确";}//2、验证余额if(account.getMoney() < money){return "用户余额不足";}//3、验证对方用户if(accountDao.selectAccount(toName) == null){return "对方用户名不存在";}//4、我方扣钱accountDao.updateAccount(fromName,-money);System.out.println(10/0);//5、对方加钱accountDao.updateAccount(toName,money);return "转账成功";} catch (SQLException throwables) {throwables.printStackTrace();}return "转账失败";}
}

9 事务

在JDBC 中,获得 Connection 对象开始事务–提交或回滚–关闭连接。其事务操作是

  • conn.setAutoCommit(false);//设置事务为手动提交
  • conn.commit();//手动提交事务
  • conn.rollback();//手动回滚事务

9.1 转账业务实现

public class AccountServiceImpl2 implements AccountService {AccountDao accountDao = new AccountDaoImpl();@Overridepublic String zhuanZhang(String fromName, String password, String toName, double money) {Connection conn = JDBCUtils.getConnection();try {           //1、验证我方用户密码Account account = accountDao.selectAccount(fromName);if(account == null){return "用户名不存在";}if(!account.getPassword().equals(password)){return "用户密码不正确";}//2、验证余额if(account.getMoney() < money){return "用户余额不足";}//3、验证对方用户if(accountDao.selectAccount(toName) == null){return "对方用户名不存在";}//4、我方扣钱accountDao.updateAccount(fromName,-money);//System.out.println(10/0);//5、对方加钱accountDao.updateAccount(toName,money);//提交事务conn.commit();return "转账成功";} catch (Exception throwables) {throwables.printStackTrace();try {//回滚事务conn.rollback();} catch (SQLException throwables) {throwables.printStackTrace();}}return "转账失败";}
}

注意:此时Service中的Connection与Dao中的Connection对象不一致,无法实现事务回滚

9.2 解决方案1:传递 Connection

  • 如果使用传递Connection,容易造成接口污染(BadSmell)。
  • 定义接口是为了更容易更换实现,而将 Connection定义在接口中,会造成污染当前接口。

9.3 解决方案2:ThreadLocal

  • 可以将整个线程中(单线程)中,存储一个共享值。
  • 线程拥有一个类似 Map 的属性,键值对结构<ThreadLocal对象,值>。

9.4 使用ThreadLocal更新JDBC工具类

/*** ThreadLocal<T>:能保存对象,能保证在同一个线程下获取到的对象是同一个*     set(T);*     get();*     remove();*/
static ThreadLocal<Connection> tl = new ThreadLocal<>();public static Connection getConnection(){//1、从ThreadLocal获取Connection//2、获取获取到了connection对象直接返回//3、创建Connection对象并存到ThreadLocal中,在进行返回Connection conn = tl.get();try {if(conn == null){conn = DriverManager.getConnection(url,username,password);tl.set(conn);}} catch (SQLException throwables) {throwables.printStackTrace();}return conn;
}

此时需要注意,如果关闭了Connetion连接,但是在ThreadLocal中还是保存着Connetion对象。下次会获取到一个已经关闭的Connection对象,所以需要从ThreadLocal中移除

9.5 事务封装

将事务的开启、提交、回滚都封装在工具类中,业务层调用即可。

 //封装事务操作的三个方法
public static void begin(){//1、获取Connection对象Connection conn = getConnection();try {//2、开启事务conn.setAutoCommit(false);} catch (SQLException throwables) {throwables.printStackTrace();}
}
public static void commit(){//1、获取Connection对象Connection conn = getConnection();try {//2、提交事务conn.commit();} catch (SQLException throwables) {throwables.printStackTrace();}finally {try {//3、关闭Connection资源conn.close();//从ThreadLocal中将connection移除掉tl.remove();} catch (SQLException throwables) {throwables.printStackTrace();}}
}
public static void rollback(){//1、获取Connection对象Connection conn = getConnection();try {//2、回滚事务conn.rollback();} catch (SQLException throwables) {throwables.printStackTrace();}finally {try {//3、关闭Connection资源conn.close();//从ThreadLocal中将connection移除掉tl.remove();} catch (SQLException throwables) {throwables.printStackTrace();}}
}

9.6 最终的转账业务

public class AccountServiceImpl2 implements AccountService {AccountDao accountDao = new AccountDaoImpl();@Overridepublic String zhuanZhang(String fromName, String password, String toName, double money) {try {//开启事务JDBCUtils.begin();//1、验证我方用户密码Account account = accountDao.selectAccount(fromName);if(account == null){return "用户名不存在";}if(!account.getPassword().equals(password)){return "用户密码不正确";}//2、验证余额if(account.getMoney() < money){return "用户余额不足";}//3、验证对方用户if(accountDao.selectAccount(toName) == null){return "对方用户名不存在";}//4、我方扣钱accountDao.updateAccount(fromName,-money);//System.out.println(10/0);//5、对方加钱accountDao.updateAccount(toName,money);//提交事务JDBCUtils.commit();return "转账成功";} catch (Exception throwables) {throwables.printStackTrace();//回滚事务JDBCUtils.rollback();}return "转账失败";}
}

10 三层架构【理解】

三层架构原理
在这里插入图片描述
三层架构下包结构
在这里插入图片描述

11 单元测试

11.1 单元测试

/*** 单元测试:对已经编写完成的类、模块、方法进行测试* 使用步骤:*  1、导入单元测试的两个jar包(与驱动包导入一致)*  2、编写方法进行测试* 常用的注解:*    @Test         单元测试的方法*    @Before       在单元测试方法之前执行*    @After        在单元测试方法之后执行*    @BeforeClass  在类加载之前执行*    @AfterClass   在类卸载之后执行** 单元测试需要注意的问题*    1、@BeforeClass测试的方法必须要加static修饰*    2、单元测试的方法不能有参数,不能有返回值*    3、不能再单元测试中写Scanner输入内容	*/
@BeforeClass
public static void testBeforeClass(){System.out.println("BeforeClass类加载的时候执行");
}@Before
public void testBefore(){System.out.println("Before在单元测试方法之前执行(自动执行)");
}@Test
public void test01(){System.out.println("单元测试");
}
@After
public void testAfter(){System.out.println("After在单元测试方法之后执行(自动执行)");
}
@AfterClass
public static void testAfterClass(){System.out.println("BeforeClass类卸载的时候执行");
}

执行结果

BeforeClass类加载的时候执行
Before在单元测试方法之前执行(自动执行)
单元测试
After在单元测试方法之后执行(自动执行)
BeforeClass类卸载的时候执行

11.2 实际应用

在实际开发过程中,我们需要对写好的DAO层代码、Service层代码进行测试

一般就是对DAO、Service层中的每一个方法进行测试

public class AccountTest {@Testpublic void testZhuanZhang(){AccountService accountService = new AccountServiceImpl2();String s = accountService.zhuanZhang("jack","123","rose",200);System.out.println(s);}
}

12 连接池【理解掌握】

12.1 Druid连接池

在程序初始化时,预先创建指定数量的数据库连接对象存储在池中。当需要连接数据库时,从连接池中取出现有连接;使用完毕后,也不会进行关闭,而是放回池中,实现复用,节省资源。

  • 创建 db.properties 配置文件。
  • 引入druid的jar 文件,添加到类路径

12.2 db.properties

driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql:///java2303?useSSL=false
username=root
password=123456
#初始化连接   (初始化连接池的,里面默认就已经存在了20个Connection连接)
initialSize=20
#最大连接数量  (当初始的20个连接不够的时候,最大会创建到50个)
maxActive=50
#最小空闲连接  (当连接池中的连接,没有被使用,就会减少到5个)
minIdle=5
#超时等待时间  (当连接数超过最大连接数,会等待5秒,如果5秒后还没有空闲连接,就会抛出异常)
maxWait=5000

12.3 最终版JDBC工具类

DruidDataSourceFactory 导包com.alibaba.druid.pool

public class JDBCUtils {//定义数据库连接池private static DataSource dataSource;//初始化连接池对象static{try {Properties properties = new Properties();InputStream in = JDBCUtils.class.getClassLoader().getResourceAsStream("db.properties");properties.load(in);//初始化连接池对象dataSource = DruidDataSourceFactory.createDataSource(properties);} catch (Exception e) {e.printStackTrace();}}//返回连接池对象public static DataSource getDataSource(){return dataSource;}//使用ThreadLocal保证Connection在同一个线程下唯一static ThreadLocal<Connection> tl = new ThreadLocal<>();public static Connection getConnection(){Connection conn = tl.get();try {if(conn == null){conn = dataSource.getConnection();tl.set(conn);}} catch (SQLException throwables) {throwables.printStackTrace();}return conn;}public static void closeAll(Connection connection, Statement statement , ResultSet rs){try {if(rs!=null)rs.close();if(statement!=null)statement.close();if(connection!=null)connection.close();} catch (SQLException throwables) {throwables.printStackTrace();}}public static void begin(){Connection conn = getConnection();try {conn.setAutoCommit(false);} catch (SQLException throwables) {throwables.printStackTrace();}}public static void commit(){Connection conn = getConnection();try {conn.commit();} catch (SQLException throwables) {throwables.printStackTrace();}finally {try {conn.close();tl.remove();} catch (SQLException throwables) {throwables.printStackTrace();}}}public static void rollback(){Connection conn = getConnection();try {conn.rollback();} catch (SQLException throwables) {throwables.printStackTrace();}finally {try {conn.close();tl.remove();} catch (SQLException throwables) {throwables.printStackTrace();}}}
}

12.4 连接池测试

public class TestDruidDataSource {public static void main(String[] args) throws SQLException {
//        Connection conn1 = JDBCUtils.getConnection();
//        System.out.println(conn1);
//        Connection conn2 = JDBCUtils.getConnection();
//        System.out.println(conn2);for (int i = 0; i < 51; i++) {Connection connection = JDBCUtils.getDataSource().getConnection();System.out.println(connection);connection.close();//并不是关闭连接,而是归还到连接池中}}
}

14 DaoUtils工具类【理解】

将Dao层中增删改的代码进行封装

13.1 工具类实现

public class DaoUtils {//更新操作(增删改) insert into emp values(?,?,?,?,?,?,?,?)public static int commonsUpdate(String sql,Object... args) throws SQLException {//1、获取数据库连接对象Connection conn = JDBCUtils.getConnection();PreparedStatement ps = null;try {//2、获取数据库操作对象ps = conn.prepareStatement(sql);//3、设置占位符的值for (int i = 0; i < args.length; i++) {ps.setObject(i+1,args[i]);}//4、执行sql语句int count = ps.executeUpdate();//5、处理结果return count;} finally {//6、关闭资源JDBCUtils.closeAll(null,ps,null);}}public static <T> List<T> commonsQuery(String sql, Class c, Object... args) throws Exception {PreparedStatement ps = null;ResultSet rs = null;try {//1、获取数据库连接对象Connection conn = JDBCUtils.getConnection();//2、获取数据库操作对象ps = conn.prepareStatement(sql);//3、设置占位符的值for (int i = 0; i < args.length; i++) {ps.setObject(i+1,args[i]);}//4、执行SQL语句rs = ps.executeQuery();//5、处理结果集List<T> list = new ArrayList<>();while(rs.next()){//通过类对象获取类的属性Field[] fields = c.getDeclaredFields();T obj = (T) c.newInstance();for (int i = 0; i < fields.length; i++) {//暴力反射fields[i].setAccessible(true);fields[i].set(obj,rs.getObject(i+1));}//将对象装到List集合中list.add(obj);}return list;}finally {JDBCUtils.closeAll(null,ps,rs);}}
}

13.2 DaoUtils工具类使用

public class EmpDaoImpl implements EmpDao {@Overridepublic int insertEmp(Emp emp) throws SQLException {String sql = "insert into emp values(?,?,?,?,?,?,?,?)";Object[] args = {emp.getEmpno(),emp.getEname(),emp.getJob(),emp.getMgr(),emp.getHireadate(),emp.getSal(),emp.getComm(),emp.getDeptno()};return DaoUtils.commonsUpdate(sql,args);}@Overridepublic int updateEmp(Emp emp) throws SQLException {String sql = "update emp set ename=?,job=?,mgr=?,hiredate=?,sal=?,comm=?,deptno=? where empno = ?";Object[] args = {emp.getEname(),emp.getJob(),emp.getMgr(),emp.getHireadate(),emp.getSal(),emp.getComm(),emp.getDeptno(),emp.getEmpno()};return  DaoUtils.commonsUpdate(sql,args);}@Overridepublic int deleteEmp(int empno) throws SQLException {String sql = "delete from emp where empno = ?";Object[] args = {empno};return DaoUtils.commonsUpdate(sql,args);}@Overridepublic List<Emp> selectAll() throws Exception {String sql = "select * from emp";return DaoUtils.commonsQuery(sql,Emp.class);}
}

14 DBUtils工具类

14.1 DBUtils简介

DbUtils是Java编程中数据库操作实用小工具,小巧、简单、实用

  • 对于数据表的查询操作,可以把结果转换为List、Array、Set等集合。便于操作。
  • 对于数据表的DML操作,也变得很简单(只需要写SQL语句)。

14.2 DbUtils核心API

  • ResultSetHandler接口:转换类型接口
    • BeanHandler类:实现类,把一条记录转换成对象
    • BeanListHandler类:实现类,把多条记录转换成List集合。
    • ScalarHandler类:实现类,适合获取一行一列的数据。
    • MapHandler类: 实现类,把一条记录转换成Map集合
    • MapListHandler类:实现类,把多条记录转换成List集合。
  • QueryRunner:执行sql语句的类
    • 增、删、改:update();
    • 查询:query();

14.3 DbUtils的使用步骤

导入jar包

  • mysql连接驱动jar包
  • 导入druid 的jar包
  • database.properties配置文件
  • 导入commons-dbutils的jar包

14.4 DBUtils使用

使用DBUtils实现增删改查

public class EmpDaoImpl implements EmpDao {@Overridepublic int insertEmp(Emp emp) throws SQLException {//1、创建QueryRunner对象//如果是更新(增删改)操作,那么就用无参的构造QueryRunner qr = new QueryRunner();//2、通过QueryRunner对象调用updateString sql = "insert into emp values(?,?,?,?,?,?,?,?)";Object[] args = {emp.getEmpno(),emp.getEname1(),emp.getJob(),emp.getMgr(),emp.getHireadate(),emp.getSal(),emp.getComm(),emp.getDeptno()};return qr.update(JDBCUtils.getConnection(),sql,args);}@Overridepublic int updateEmp(Emp emp) throws SQLException {//1、创建QueryRunner对象//如果是更新(增删改)操作,那么就用无参的构造QueryRunner qr = new QueryRunner();//2、通过QueryRunner对象调用updateString sql = "update emp set ename=?,job=?,mgr=?,hiredate=?,sal=?,comm=?,deptno=? where empno = ?";Object[] args = {emp.getEname1(),emp.getJob(),emp.getMgr(),emp.getHireadate(),emp.getSal(),emp.getComm(),emp.getDeptno(),emp.getEmpno()};return qr.update(JDBCUtils.getConnection(),sql,args);}@Overridepublic int deleteEmp(int empno) throws SQLException {//1、创建QueryRunner对象//如果是更新(增删改)操作,那么就用无参的构造QueryRunner qr = new QueryRunner();//2、通过QueryRunner对象调用updateString sql = "delete from emp where empno = ?";Object[] args = {empno};return qr.update(JDBCUtils.getConnection(),sql,args);}@Overridepublic List<Emp> selectAll() throws Exception {//1、创建QueryRunner对象//如果是查询操作,那么就用有参的构造,传递连接池对象(使用完成之后QueryRunner会自动关闭(回收))QueryRunner qr = new QueryRunner(JDBCUtils.getDataSource());//2、通过QueryRunner对象调用queryString sql = "select empno,ename ename1,job,mgr,hiredate hireadate,sal,comm,deptno from emp";//如果是集合就创建BeanListHandler对象,如果是实体类就创建BeanHandler对象List<Emp> empList = qr.query(sql, new BeanListHandler<Emp>(Emp.class));return empList;}@Overridepublic Emp selectOne(int empno) throws SQLException {//1、创建QueryRunner对象//如果是查询操作,那么就用有参的构造,传递连接池对象(使用完成之后QueryRunner会自动关闭(回收))QueryRunner qr = new QueryRunner(JDBCUtils.getDataSource());//2、通过QueryRunner对象调用queryString sql = "select * from emp where empno = ?";Object[] args = {empno};Emp emp = qr.query(sql, new BeanHandler<Emp>(Emp.class),args);return emp;}@Overridepublic long count() throws SQLException {//1、创建QueryRunner对象//如果是查询操作,那么就用有参的构造,传递连接池对象(使用完成之后QueryRunner会自动关闭(回收))QueryRunner qr = new QueryRunner(JDBCUtils.getDataSource());String sql = "select count(*) from emp";Long count = qr.query(sql, new ScalarHandler<Long>());return count;}
}

14.5 字段与属性名不一致

如果数据库的字段名与实体类中的属性名不一致,则无法完成映射,值会显示null

2种解决:

  • 在查询语句中取别名

后期自定义映射

@Override
public List<Emp> selectAll() throws Exception {//1、创建QueryRunner对象//如果是查询操作,那么就用有参的构造,传递连接池对象(使用完成之后QueryRunner会自动关闭(回收))QueryRunner qr = new QueryRunner(JDBCUtils.getDataSource());//2、通过QueryRunner对象调用query//通过别名进行数据映射String sql = "select empno,ename ename1,job,mgr,hiredate hireadate,sal,comm,deptno from emp";//如果是集合就创建BeanListHandler对象,如果是实体类就创建BeanHandler对象List<Emp> empList = qr.query(sql, new BeanListHandler<Emp>(Emp.class));return empList;
}
public class Emp {private Integer empno;private String ename1; //这个与数据库字段不一致private String job;private Integer mgr;private Date hireadate; //这个与数据库字段不一致private Double sal;private Double comm;private Integer deptno;//省略set、get、构造方法
}

15 Lombok插件

Lombok简化编写类的构造方法、setget方法以及toString方法

使用步骤

安装插件,需要重启目前IDEA版本已经自带此插件无需安装
在这里插入图片描述
|
开启IEDA注解可用需要在setting中进行设置还需要在setting for newproject中设置
在这里插入图片描述
    |
新版本使用lombok插件需要添加配置需要在setting中进行设置还需要在setting for newproject中设置
-Djps.track.ap.dependencies=false
在这里插入图片描述
导入jar包
在这里插入图片描述
编写实体类
在这里插入图片描述

相关文章:

Java数据库部分(MySQL+JDBC)(二、JDBC超详细学习笔记)

文章目录1 JDBC&#xff08;Java Database Connectivity&#xff09;1.1 什么是 JDBC&#xff1f;1.2 JDBC 核心思想2 JDBC开发步骤【重点】2.0 环境准备2.1 注册数据库驱动2.2 获取数据库的连接2.3 获取数据库操作对象Statement2.4 通过Statement对象执行SQL语句2.5 处理返回结…...

vue3生命周期

一、Vue3中的生命周期 1、setup() : 开始创建组件之前&#xff0c;在 beforeCreate 和 created 之前执行&#xff0c;创建的是 data 和 method 2、onBeforeMount() : 组件挂载到节点上之前执行的函数&#xff1b; 3、onMounted() : 组件挂载完成后执行的函数&#xff1b; 4、…...

Python学习笔记10:开箱即用

开箱即用 模块 python系统路径 import sys, pprint pprint.pprint(sys.path) [,D:\\Program Files\\Python\\Lib\\idlelib,D:\\Program Files\\Python\\python310.zip,D:\\Program Files\\Python\\DLLs,D:\\Program Files\\Python\\lib,D:\\Program Files\\Python,D:\\Progr…...

详解JAVA反射

目录 1.概述 2.获取Class对象 3.API 3.1.实例化对象 3.2.方法 3.3.属性 1.概述 反射&#xff0c;JAVA提供的一种在运行时获取类的信息并动态操作类的能力。JAVA反射允许我们在运行时获取类的属性、方法、构造函数等信息&#xff0c;并能够动态地操作它们。 2.获取Class…...

在nestjs中进行typeorm cli迁移(migration)的配置

在nestjs中进行typeorm cli迁移(migration)的配置 在学习nestjs过程中发现typeorm的迁移配置十分麻烦,似乎许多方法都是旧版本的配置&#xff0c;无法直接使用. 花了挺长时间总算解决了这个配置问题. db.config.ts 先创建db.config.ts, 该文件export了两个对象&#xff0c;其…...

前端工程构建问题汇总

1.less less-loader安装失败问题 npm install less-loader --save --legacy-peer-deps 加上–legacy-peer-deps就可以了 在NPM v7中&#xff0c;现在默认安装peerDependencies&#xff0c;这会导致版本冲突&#xff0c;从而中断安装过程。 –legacy-peer-deps标志是在v7中引…...

某马程序员NodeJS速学笔记

文章目录前言一、什么是Node.js?二、fs文件系统模块三、Http模块四、模块化五、开发属于自己的包模块加载机制六、Express1.初识ExpressGET/POSTnodemon2.路由模块化3.中间件中间件分类自定义中间件4. 跨域问题七、Mysql模块安装与配置基本使用Web开发模式Session认证JWT八、m…...

SpringMVC DispatcherServlet源码(6) 完结 静态资源原理

阅读源码&#xff0c;分析静态资源处理器相关组件&#xff1a; 使用SimpleUrlHandlerMapping管理url -> 处理器映射关系spring mvc使用WebMvcConfigurationSupport注入SimpleUrlHandlerMapping组件DelegatingWebMvcConfiguration可以使用WebMvcConfigurer的配置静态资源url…...

2023年全国最新会计专业技术资格精选真题及答案9

百分百题库提供会计专业技术资格考试试题、会计考试预测题、会计专业技术资格考试真题、会计证考试题库等&#xff0c;提供在线做题刷题&#xff0c;在线模拟考试&#xff0c;助你考试轻松过关。 四、材料题 1.某企业为增值税一般纳税人&#xff0c;2019年12月初“应付职工薪酬…...

Web3中文|把Web3装进口袋,Solana手机Saga有何魔力?

2月23日&#xff0c;Solana Web3手机Saga发布新的消息&#xff0c;将推出NFT铸造应用程序Minty Fresh。在Minty Fresh&#xff0c;用户仅需轻点并完成拍摄&#xff0c;就可以直接在手机中进行NFT铸造&#xff0c;并在几秒钟内将其转换为链上NFT&#xff0c;NFT还可以发布在 Ins…...

【配电网优化】基于串行和并行ADMM算法的配电网优化研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…...

数据结构初阶 -- 顺序表

数据结构初阶 链表的讲解 目录 一. 线性表 1.1 定义 1.2 特点 二. 顺序表 2.1 定义 2.2 代码 2.3 功能需求 2.4 静态顺序表的特点以及缺点 2.5 动态的顺序表 2.6 动态顺序表接口的实现 三. 代码 头文件 主文件 一. 线性表 1.1 定义 线性表&#xff08;linear li…...

uniapp:3分钟搞定在线推送uni.createPushMessage,uni.onPushMessage

安卓端 在线推送功能演示&#xff1a; 1、dcloud后台申请开通uniPush dcloud后台 &#xff08;1&#xff09;&#xff1a;找到我的应用 &#xff08;2&#xff09;&#xff1a;点进去后&#xff0c;各平台信息&#xff0c;点击新增 &#xff08;3&#xff09;&#xff1a;填…...

C/C++开发,无可避免的多线程(篇一).跨平台并行编程姗姗来迟

一、编译环境准备 在正式进入c/c多线程编程系列之前&#xff0c;先来搭建支持多线程编译的编译环境。 1.1 MinGW&#xff08;win&#xff09; 进入Downloads - MinGW-w64下载页面&#xff0c;选择MinGW-w64-builds跳转下载&#xff0c; 再次进行跳转&#xff1a; 然后进入下载页…...

如何把照片的底色修改为想要的颜色

如何给照片更换底色&#xff1f;其实有可以一键给照片更换底色的 APP &#xff0c;但是几乎都要收费。如果想要免费的给照片更换底色的话&#xff0c;分享两种简单便捷的方法给你。掌握了这项技能&#xff0c;以后就不用店花钱处理啦&#xff01;1、免费&#xff01;线上快速 给…...

【高效办公】批量生成固定模板的文件夹名称

老师让你按照他的要求生成每位学生的文件夹,你是学委,让你马上完成该任务,但你又不想是手动一个一个码字,因此聪明的你就看到了本篇文章啦!!! 虽说一个人懒惰,并不是好的事情。 但这个似乎合情合理啊~ 然后,就动手想办法,一开始就真的打算码字了。。 思路 在实际开…...

redis的集群方式

1.主从复制 主从复制原理&#xff1a; 从服务器连接主服务器&#xff0c;发送SYNC命令&#xff1b; 主服务器接收到SYNC命名后&#xff0c;开始执行BGSAVE命令生成RDB文件并使用缓冲区记录此后执行的所有写命令&#xff1b; 主服务器BGSAVE执行完后&#xff0c;向所有从服务…...

温控负荷的需求响应潜力评估及其协同优化管理研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…...

模电学习9. MOS管使用入门

模电学习9. MOS管使用入门一、mos管理简介1. 简介2. mos管理的特点3. MOS管的工作状态&#xff08;1&#xff09;放大功能&#xff08;2&#xff09;截止区&#xff08;3&#xff09;饱和区3. Mos管的分类&#xff08;1&#xff09;按照工作模式分类&#xff1a;&#xff08;2&…...

【算法】【数组与矩阵模块】正数组中累加和为给定值的最长子数组长度,空间复杂度O(1)解法

目录前言问题介绍解决方案代码编写java语言版本c语言版本c语言版本思考感悟写在最后前言 当前所有算法都使用测试用例运行过&#xff0c;但是不保证100%的测试用例&#xff0c;如果存在问题务必联系批评指正~ 在此感谢左大神让我对算法有了新的感悟认识&#xff01; 问题介绍 …...

AI Agent与Agentic AI:原理、应用、挑战与未来展望

文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例&#xff1a;使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例&#xff1a;使用OpenAI GPT-3进…...

ESP32读取DHT11温湿度数据

芯片&#xff1a;ESP32 环境&#xff1a;Arduino 一、安装DHT11传感器库 红框的库&#xff0c;别安装错了 二、代码 注意&#xff0c;DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具

文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...

DBAPI如何优雅的获取单条数据

API如何优雅的获取单条数据 案例一 对于查询类API&#xff0c;查询的是单条数据&#xff0c;比如根据主键ID查询用户信息&#xff0c;sql如下&#xff1a; select id, name, age from user where id #{id}API默认返回的数据格式是多条的&#xff0c;如下&#xff1a; {&qu…...

WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)

一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解&#xff0c;适合用作学习或写简历项目背景说明。 &#x1f9e0; 一、概念简介&#xff1a;Solidity 合约开发 Solidity 是一种专门为 以太坊&#xff08;Ethereum&#xff09;平台编写智能合约的高级编…...

关于 WASM:1. WASM 基础原理

一、WASM 简介 1.1 WebAssembly 是什么&#xff1f; WebAssembly&#xff08;WASM&#xff09; 是一种能在现代浏览器中高效运行的二进制指令格式&#xff0c;它不是传统的编程语言&#xff0c;而是一种 低级字节码格式&#xff0c;可由高级语言&#xff08;如 C、C、Rust&am…...

精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南

精益数据分析&#xff08;97/126&#xff09;&#xff1a;邮件营销与用户参与度的关键指标优化指南 在数字化营销时代&#xff0c;邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天&#xff0c;我们将深入解析邮件打开率、网站可用性、页面参与时…...

Typeerror: cannot read properties of undefined (reading ‘XXX‘)

最近需要在离线机器上运行软件&#xff0c;所以得把软件用docker打包起来&#xff0c;大部分功能都没问题&#xff0c;出了一个奇怪的事情。同样的代码&#xff0c;在本机上用vscode可以运行起来&#xff0c;但是打包之后在docker里出现了问题。使用的是dialog组件&#xff0c;…...

力扣-35.搜索插入位置

题目描述 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...

rnn判断string中第一次出现a的下标

# coding:utf8 import torch import torch.nn as nn import numpy as np import random import json""" 基于pytorch的网络编写 实现一个RNN网络完成多分类任务 判断字符 a 第一次出现在字符串中的位置 """class TorchModel(nn.Module):def __in…...