首页 >> 大全

jdbc学习笔记以及DAO层设计

2023-12-23 大全 72 作者:考证青年

1 简介和基础使用 1.1 简介

JDBC:是sun发布的一个java程序和数据库之间通信的规范(接口)

各大数据库厂商去实现JDBC规范(实现类),将这些实现类打成压缩包,就是所谓的jar包

比如:

1.2 创建连接

url:防止中文乱码,加上参数=true&=utf-8。url的标准格式为:jdbc:mysql://ip:端口号/数据库名称?参数列表

public class Demo01 {public static void main(String[] args) throws ClassNotFoundException, SQLException {
//        1.添加jar包
//        2.加载驱动Class.forName("com.mysql.jdbc.Driver");
//       3.通过驱动管理器获取连接对象
//        3-1 准备urlString url = "jdbc:mysql://localhost:3306/fruitdb";
//        3-2 准备用户名String usr = "root";
//        3-3 准备密码String pwd = "root";Connection connection = DriverManager.getConnection(url,usr,pwd);System.out.println("conn = "+connection);}
}

1.3 添加数据

先建立连接之后,得到对象,这个对象相当于建立了一条连接数据库和java之间的马路,通过对象得到预处理对象,这个对象相当于马路上的一辆车,用于运送真实的数据。

在SQL语句中,使用?当做占位符,再通过预处理对象的set方法为占位符填充内容

最后,记得关闭连接,先关闭预处理对象,再关闭连接对象

public class Demo02 {public static void main(String[] args) throws ClassNotFoundException, SQLException {
//        1.加载驱动Class.forName("com.mysql.jdbc.Driver");//       2.通过驱动管理器获取连接对象
//        2-1 准备url
//        url表示和数据库通信的地址,如果需要带参数,则需要使用?进行连接
//        如果使用多个参数,多个参数之间使用&连接String url = "jdbc:mysql://localhost:3306/fruitdb?useSSL=false&useUnicode=true&characterEncoding=utf-8";
//        2-2 准备用户名String usr = "root";
//        2-3 准备密码String pwd = "root";Connection connection = DriverManager.getConnection(url,usr,pwd);//        3. 编写SQL语句
//        id,fname,price,fcount,remarkString sql = "insert into t_fruit value(0,?,?,?,?)";//        4.创建预处理对象PreparedStatement psmt = connection.prepareStatement(sql);
//        5.填充参数psmt.setString(1,"榴莲");psmt.setInt(2,15);psmt.setInt(3,100);psmt.setString(4,"榴莲是一种神奇的水果");
//        6.执行更新(增删改),返回影响行数int i = psmt.executeUpdate();System.out.println(i>0?"添加成功!":"添加失败");
//        7.释放资源psmt.close();connection.close();}
}

1.4 更新和删除数据

和添加数据步骤都一样,最后都是预处理对象调用方法并返回影响行数。因此更新与修改和添加的不同就是SQL语句和预处理设置SQL中的占位符的值而已。

更新:

 Fruit fruit = new Fruit(33, "猕猴桃", "猕猴桃啊猕猴桃");Class.forName("com.mysql.jdbc.Driver");String url = "jdbc:mysql://localhost:3306/fruitdb?useSSL=false&useUnicode=true&characterEncoding=utf8";Connection connection = DriverManager.getConnection(url, "root", "root");String sql = "update t_fruit set fname=?,remark = ? where fid = ?";PreparedStatement psmt = connection.prepareStatement(sql);psmt.setString(1, fruit.getFname());psmt.setString(2, fruit.getRemark());psmt.setInt(3,fruit.getFid());int i = psmt.executeUpdate();System.out.println(i>0?"修改成功":"修改失败");psmt.close();connection.close();

删除:

  Class.forName("com.mysql.jdbc.Driver");String url = "jdbc:mysql://localhost:3306/fruitdb?useSSL=false&useUnicode=true&characterEncoding=utf8";Connection connection = DriverManager.getConnection(url, "root", "root");String sql = "delete from t_fruit where fid = ?";PreparedStatement psmt = connection.prepareStatement(sql);psmt.setInt(1,33);int i = psmt.executeUpdate();System.out.println(i>0?"删除成功":"删除失败");psmt.close();connection.close();

1.5 增删更新总结 1.5.1 步骤: 加载驱动通过驱动获取连接对象,连接对象相当于连接java与数据库之间的一条马路编写SQL语句,语句中使用占位符创建数据预处理对象,数据预处理对象相当于马路上运送SQL语句的小车填充预处理数据对象的参数执行更新参数,对于增删查都是执行对象的方法,返回影响行数关闭资源,关闭对象和对象 1.6 查询

查询与增删改的不同之处在于:

例如:查询所有数据

//        1.加载驱动Class.forName("com.mysql.jdbc.Driver");String url = "jdbc:mysql://localhost:3306/fruitdb?useSSL=false&useUnicode=true&characterEncoding=utf8";
//        2.获取连接对象Connection connection = DriverManager.getConnection(url, "root", "root");//        3.编写SQL语句String sql = "select * from t_fruit";
//        4.创建预处理命令对象PreparedStatement psmt = connection.prepareStatement(sql);
//        5.填充参数,这里没有,略
//        6.执行查询,返回结果集ResultSet res = psmt.executeQuery();List<Fruit> list = new ArrayList<>();
//        7.处理结果集
//        判断下一行是否有数据,并且指针指到下一行while(res.next()){
//            表示当前行第一列的数据,因为这一列是int类型的数据,所以使用getIntint fid = res.getInt(1);
//            也可以通过结果集的列名String fname = res.getString("fname");int price = res.getInt(3);int fcount = res.getInt("fcount");String remark = res.getString("remark");Fruit fruit = new Fruit(fid,fname,price,fcount,remark);list.add(fruit);}
//        8.释放资源res.close();psmt.close();connection.close();for (Fruit fruit : list) {System.out.println(fruit);}

查询指定的一条记录:

		Class.forName("com.mysql.jdbc.Driver");String url = "jdbc:mysql://localhost:3306/fruitdb?useSSL=false&useUnicode=true&characterEncoding=utf8";Connection connection = DriverManager.getConnection(url, "root", "root");//        编写SQL语句String sql = "select * from t_fruit where fid = ?";
//        创建预处理命令对象PreparedStatement psmt = connection.prepareStatement(sql);
//        填充参数psmt.setInt(1,2);
//        由于fid是主键,所以可以不使用列表,这里改一下
//        执行查询,返回结果集ResultSet res = psmt.executeQuery();
//        判断下一行是否有数据,并且指针指到下一行if(res.next()){
//            表示当前行第一列的数据,因为这一列是int类型的数据,所以使用getIntint fid = res.getInt(1);
//            也可以通过结果集的列名String fname = res.getString("fname");int price = res.getInt(3);int fcount = res.getInt("fcount");String remark = res.getString("remark");Fruit fruit = new Fruit(fid,fname,price,fcount,remark);System.out.println(fruit);}
//        释放资源res.close();psmt.close();connection.close();

查询总记录条数:

        Class.forName("com.mysql.jdbc.Driver");String url = "jdbc:mysql://localhost:3306/fruitdb?useSSL=false&useUnicode=true&characterEncoding=utf8";Connection connection = DriverManager.getConnection(url, "root", "root");//        编写SQL语句String sql = "select count(*) from t_fruit";
//        创建预处理命令对象PreparedStatement psmt = connection.prepareStatement(sql);
//        执行查询,返回结果集
//        这里返回的是一个一行一列的数据ResultSet res = psmt.executeQuery();
//        判断下一行是否有数据,并且指针指到下一行if(res.next()){int count = res.getInt(1);System.out.println("总记录条数:"+count);}
//        释放资源res.close();psmt.close();connection.close();

1.7 查询总结 1.7.1 查询与增删改的不同之处在于: 1.7.2 步骤: 加载驱动通过驱动获取连接对象,连接对象相当于连接java与数据库之间的一条马路编写SQL语句,语句中使用占位符创建数据预处理对象,数据预处理对象相当于马路上运送SQL语句的小车填充预处理数据对象的参数执行更新参数,执行方法返回结果集关闭资源,关闭对象、对象和对象 1.8 批处理

如果一次性添加大量数据,若每一条数据都单独执行一次方法,那么效率很低下,所以可以采用批处理的方法来提高处理的效率。

批处理操作的步骤:

如果执行批处理任务,需要在添加一个参数: ents=true将数据加入一个批次:psmt.();执行批处理:int[] batch = psmt.();如果继续进行处理,需要进行clear:psmt.clear();

package com.zylai.jdbc01;import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Arrays;/*** @Author: Zhao YunLai* @Date: 2022/06/21/12:54* @Description: 添加*/
public class DemoAddBatch {public static void main(String[] args) throws ClassNotFoundException, SQLException {
//        1.加载驱动Class.forName("com.mysql.jdbc.Driver");//       2.通过驱动管理器获取连接对象
//        批处理操作一
//        如果执行批处理任务,需要添加一个参数: rewriteBatchedStatements=trueString url = "jdbc:mysql://localhost:3306/fruitdb?rewriteBatchedStatements=true&useSSL=false&useUnicode=true&characterEncoding=utf-8";
//        2-2 准备用户名String usr = "root";
//        2-3 准备密码String pwd = "root";Connection connection = DriverManager.getConnection(url,usr,pwd);
//        3. 编写SQL语句String sql = "insert into t_fruit value(0,?,?,?,?)";//        4.创建预处理对象PreparedStatement psmt = connection.prepareStatement(sql);
//        5.填充参数for (int i = 0; i < 1000; i++) {psmt.setString(1,"榴莲"+i);psmt.setInt(2,15);psmt.setInt(3,100);psmt.setString(4,"榴莲是一种神奇的水果"+i);//            批处理操作二:将数据加入一个批次psmt.addBatch();
//            每一百个处理一次,分批次处理,每次执行完要记得清空一下队列if(i%100==0){psmt.executeBatch();
//                记得clear一下psmt.clearBatch();}}
//        批处理操作三int[] batch = psmt.executeBatch();System.out.println(Arrays.toString(batch));//        7.释放资源psmt.close();connection.close();}
}

2 工程 2.1 需求概述和整体框架

以实现一个水果库存系统为例,这个系统需要提供水果的增删改查操作

=================欢迎使用水果库存系统=====================
1.查看水果库存列表
2.添加水果库存信息
3.查看特定水果库存信息
4.水果下架
5.退出
======================================================

整体框架如下图,在里面写调用DAO层的方法,在dao里面写持久层方法,提供接口及其实现类,pojo写实体类,view是客户端视图操作。

2.2 最初版本 2.2.1 pojo

pojo包下的Fruit类

package com.zylai.fruit01.pojo;/*** @Author: Zhao YunLai* @Date: 2022/06/21/17:33* @Description:*/
public class Fruit {private Integer fid;private String fname;private Integer price;private Integer fcount;private String remark;public Fruit(){}public Fruit(Integer fid, String fname, String remark) {this.fid = fid;this.fname = fname;this.remark = remark;}public Fruit(Integer fid, String fname, Integer price, Integer fcount, String remark) {this.fid = fid;this.fname = fname;this.price = price;this.fcount = fcount;this.remark = remark;}@Overridepublic String toString() {return fid+"\t\t"+fname+"\t\t"+price+"\t\t"+fcount+"\t\t"+remark;}public Integer getFid() {return fid;}public void setFid(Integer fid) {this.fid = fid;}public String getFname() {return fname;}public void setFname(String fname) {this.fname = fname;}public Integer getPrice() {return price;}public void setPrice(Integer price) {this.price = price;}public Integer getFcount() {return fcount;}public void setFcount(Integer fcount) {this.fcount = fcount;}public String getRemark() {return remark;}public void setRemark(String remark) {this.remark = remark;}
}

2.2.2 dao

接口

package com.zylai.fruit01.dao;import com.zylai.fruit01.pojo.Fruit;import java.util.List;/*** @Author: Zhao YunLai* @Date: 2022/06/21/18:02* @Description:*/
public interface FruitDAO {
//    查询库存列表List<Fruit> getFruitList();//    根据水果名称查询库存Fruit getFruitByName(String fname);
//    新增水果库存boolean addFruit(Fruit fruit);
//    修改库存boolean updateFruit(Fruit fruit);
//    根据水果名称删除记录boolean deleteFruitByName(String fname);}

实现类

注意到这个实现类存在着大量的代码冗余,之后的优化主要对这个类进行优化

package com.zylai.fruit01.dao.impl;import com.zylai.fruit01.dao.FruitDAO;
import com.zylai.fruit01.pojo.Fruit;import java.sql.*;
import java.util.ArrayList;
import java.util.List;/*** @Author: Zhao YunLai* @Date: 2022/06/21/18:05* @Description:*/
public class FruitDAOImpl implements FruitDAO {private final String DRIVER = "com.mysql.jdbc.Driver";private final String URL = "jdbc:mysql://localhost:3306/fruitdb?useUnicode=true&characterEncoding=utf-8&useSSL=false";private final String USER = "root";private final String PWD = "root";//    查询所有的库存信息@Overridepublic List<Fruit> getFruitList() {List<Fruit> fruitList = new ArrayList<>();Connection connection = null;PreparedStatement psmt = null;ResultSet rs = null;try {
//            1.加载驱动Class.forName(DRIVER);
//            2.通过驱动获取连接对象connection= DriverManager.getConnection(URL, USER, PWD);
//            3.编写SQL语句String sql = "select * from t_fruit";
//            4.创建预处理命令对象psmt = connection.prepareStatement(sql);
//            5.执行查询rs = psmt.executeQuery();
//            6.解析查询while(rs.next()){int fid = rs.getInt(1);String fname = rs.getString(2);int price = rs.getInt(3);int fcount = rs.getInt(4);String remark = rs.getString(5);Fruit fruit = new Fruit(fid,fname,price,fcount,remark);fruitList.add(fruit);}} catch (ClassNotFoundException | SQLException e) {e.printStackTrace();}finally {try {if(rs!=null){rs.close();}if(psmt!=null){psmt.close();}if(connection!=null){connection.close();}} catch (SQLException e) {e.printStackTrace();}}return fruitList;}//    通过水果名称查询记录@Overridepublic Fruit getFruitByName(String fname) {Connection connection = null;PreparedStatement psmt = null;ResultSet rs = null;try {Class.forName(DRIVER);connection = DriverManager.getConnection(URL,USER,PWD);String sql = "select * from t_fruit where fname=?";psmt = connection.prepareStatement(sql);psmt.setString(1,fname);rs= psmt.executeQuery();if(rs.next()){int fid = rs.getInt("fid");int price = rs.getInt("price");int fcount = rs.getInt("fcount");String remark = rs.getString("remark");return new Fruit(fid,fname,price,fcount,remark);}} catch (ClassNotFoundException | SQLException e) {e.printStackTrace();}finally {try{if(rs!=null){rs.close();}if(psmt!=null){psmt.close();}if(connection!=null){connection.close();}} catch (SQLException e) {e.printStackTrace();}}return null;}//    添加水果@Overridepublic boolean addFruit(Fruit fruit) {Connection connection = null;PreparedStatement psmt = null;try {Class.forName(DRIVER);connection = DriverManager.getConnection(URL,USER,PWD);String sql = "insert into t_fruit values(0,?,?,?,?)";psmt = connection.prepareStatement(sql);psmt.setString(1,fruit.getFname());psmt.setInt(2,fruit.getPrice());psmt.setInt(3,fruit.getFcount());psmt.setString(4,fruit.getRemark());int i = psmt.executeUpdate();return i>0;} catch (ClassNotFoundException | SQLException e) {e.printStackTrace();}finally {try {if(psmt!=null){psmt.close();}if(connection!=null){connection.close();}} catch (SQLException e) {e.printStackTrace();}}return false;}//    更新水果库存信息@Overridepublic boolean updateFruit(Fruit fruit) {Connection connection = null;PreparedStatement psmt = null;try {Class.forName(DRIVER);connection = DriverManager.getConnection(URL,USER,PWD);String sql = "update t_fruit set fname=?,price=?,fcount=?,remark=? where fid=?";psmt = connection.prepareStatement(sql);psmt.setString(1, fruit.getFname());psmt.setInt(2,fruit.getPrice());psmt.setInt(3,fruit.getFcount());psmt.setString(4,fruit.getRemark());psmt.setInt(5,fruit.getFid());int i = psmt.executeUpdate();return i>0;} catch (ClassNotFoundException | SQLException e) {e.printStackTrace();}finally {try {if(psmt!=null){psmt.close();}if(connection!=null){connection.close();}} catch (SQLException e) {e.printStackTrace();}}return false;}//    删除水果@Overridepublic boolean deleteFruitByName(String fname) {Connection connection = null;PreparedStatement psmt = null;try {Class.forName(DRIVER);connection = DriverManager.getConnection(URL,USER,PWD);String sql = "delete from t_fruit where fname=?";psmt = connection.prepareStatement(sql);psmt.setString(1,fname);return psmt.executeUpdate()>0;} catch (ClassNotFoundException | SQLException e) {e.printStackTrace();}finally {try {if(psmt!=null){psmt.close();}if(connection!=null){connection.close();}} catch (SQLException e) {e.printStackTrace();}}return false;}
}

2.2.3

处理请求层,由于业务比较简单,所以一些业务都在里面进行

package com.zylai.fruit01.controller;import com.zylai.fruit01.dao.FruitDAO;
import com.zylai.fruit01.dao.impl.FruitDAOImpl;
import com.zylai.fruit01.pojo.Fruit;import java.util.List;
import java.util.Scanner;/*** @Author: Zhao YunLai* @Date: 2022/06/21/17:35* @Description:*/
public class Menu {Scanner input = new Scanner(System.in);private FruitDAO fruitDAO = new FruitDAOImpl();public int showMainMenu(){System.out.println("=================欢迎使用水果库存系统=====================");System.out.println("1.查看水果库存列表");System.out.println("2.添加水果库存信息");System.out.println("3.查看特定水果库存信息");System.out.println("4.水果下架");System.out.println("5.退出");System.out.println("======================================================");System.out.print("请选择:");int res = input.nextInt();while(true){if(res<1||res>5){System.out.println("请在1~5当中进行选择!");System.out.print("请选择:");res = input.nextInt();}else{break;}}return res;}
//    显示所有水果库存public void showFruitList(){List<Fruit> fruitList = fruitDAO.getFruitList();System.out.println("------------------------------------------------------");System.out.println("编号\t\t名称\t\t单价\t\t库存\t\t备注\t\t");if(fruitList==null||fruitList.size()<=0){System.out.println("对不起,库存为空");}else{for (Fruit fruit : fruitList) {System.out.println(fruit);}}System.out.println("------------------------------------------------------");}//    添加水果库存信息 -- 业务方法public void addFruit(){System.out.print("请输入水果名称:");String fname = input.next();
//       判断是添加库存还是添加一个新水果Fruit fruitByName = fruitDAO.getFruitByName(fname);if(fruitByName==null){//说明库存中没有这个水果,直接添加System.out.print("请输入水果单价:");int price = input.nextInt();System.out.print("请输入水果库存量:");int fcount = input.nextInt();System.out.print("请输入水果备注:");String remark = input.next();fruitByName = new Fruit(0,fname,price,fcount,remark);
//            调用daofruitDAO.addFruit(fruitByName);}else{System.out.print("请输入追加的库存量:");int fcount = input.nextInt();fruitByName.setFcount(fruitByName.getFcount()+fcount);fruitDAO.updateFruit(fruitByName);}}
//    查看特定水果库存信息public void showFruitInfo(){System.out.print("请输入水果名称:");String fname = input.next();Fruit fruit = fruitDAO.getFruitByName(fname);if(fruit == null){System.out.println("对不起,没有找到指定的水果库存记录");}else{System.out.println("------------------------------------------------------");System.out.println("编号\t\t名称\t\t单价\t\t库存\t\t备注\t\t");System.out.println(fruit);System.out.println("------------------------------------------------------");}}public void deleteFruit(){System.out.print("请输入水果名称:");String fname = input.next();Fruit fruit = fruitDAO.getFruitByName(fname);if(fruit==null){System.out.println("对不起,没有找到要下架的水果信息");}else{System.out.print("是否下架?(Y/N)");String str = input.next();if("y".equalsIgnoreCase(str)){boolean b = fruitDAO.deleteFruitByName(fname);if(b){System.out.println("下架成功!");}}}}
//    退出public boolean exit(){do{System.out.print("是否确认退出?(Y/N):");String res = input.next();if("y".equalsIgnoreCase(res)){return true;}else if("n".equalsIgnoreCase(res)){return false;}}while(true);}
}

2.2.4 view

package com.zylai.fruit01.view;import com.zylai.fruit01.controller.Menu;/*** @Author: Zhao YunLai* @Date: 2022/06/21/17:36* @Description:*/
public class Client {public static void main(String[] args) {Menu menu = new Menu();boolean flag = false;while(!flag){//        调用主菜单的方法int slt = menu.showMainMenu();switch(slt){case 1:menu.showFruitList();break;case 2:menu.addFruit();break;case 3:menu.showFruitInfo();break;case 4:menu.deleteFruit();break;case 5:flag = menu.exit();break;}}System.out.println("谢谢使用");}
}

2.2.5 最初版总结

代码的冗余量很大,主要是dao层的实现类,每个方法都要重复建立驱动、获取连接、关闭资源等操作。

_多层级笔记软件_笔记布局图

2.3 改进:对于中获取连接操作以及释放资源操作做了提取

这次的改进就是对于中获取连接操作以及释放资源操作做了提取

如下:将加载驱动获取连接封装在方法中,将关闭资源封装在close方法中

    private Connection getConnection(){try {//       1.加载驱动Class.forName(DRIVER);//            2.通过驱动获取连接对象return DriverManager.getConnection(URL, USER, PWD);} catch (ClassNotFoundException | SQLException e) {e.printStackTrace();}return null;}private void close(Connection connection,PreparedStatement psmt, ResultSet rs){try {if(rs!=null){rs.close();}if(psmt!=null){psmt.close();}if(connection!=null){connection.close();}} catch (SQLException e) {e.printStackTrace();}}//实际中使用
//    通过水果名称查询记录@Overridepublic Fruit getFruitByName(String fname) {Connection connection = null;PreparedStatement psmt = null;ResultSet rs = null;try {connection = getConnection();String sql = "select * from t_fruit where fname=?";psmt = connection.prepareStatement(sql);psmt.setString(1,fname);rs= psmt.executeQuery();if(rs.next()){int fid = rs.getInt("fid");int price = rs.getInt("price");int fcount = rs.getInt("fcount");String remark = rs.getString("remark");return new Fruit(fid,fname,price,fcount,remark);}} catch ( SQLException e) {e.printStackTrace();}finally {close(connection,psmt,rs);}return null;}

2.4 改进:设置抽象类,并抽取增删改操作

可以看到下面这个三个操作的步骤完全一样,不一样的地方只是SQL语句和psmt设置的参数

因此可以将这些相同的操作抽取出来

此外,这里只是涉及到一个表的操作,当有多个表时,就涉及到多个dao类,而这多个dao类都需要这共同的驱动地址,数据库url,用户名,密码,获取驱动方法,关闭资源的方法,抽取增删改操作方法。

因此,加入一个抽象类,里面提供公共的常量和方法,并且把抽取的增删改方法加入

package com.zylai.fruit.dao.base;import java.sql.*;/*** @Author: Zhao YunLai* @Date: 2022/06/21/21:45* @Description:*/
public abstract class BaseDAO {protected final String DRIVER = "com.mysql.jdbc.Driver";protected final String URL = "jdbc:mysql://localhost:3306/fruitdb?useUnicode=true&characterEncoding=utf-8&useSSL=false";protected final String USER = "root";protected final String PWD = "root";protected Connection getConnection(){try {//       1.加载驱动Class.forName(DRIVER);//            2.通过驱动获取连接对象return DriverManager.getConnection(URL, USER, PWD);} catch (ClassNotFoundException | SQLException e) {e.printStackTrace();}return null;}//    关闭资源protected  void close(Connection connection, PreparedStatement psmt, ResultSet rs){try {if(rs!=null){rs.close();}if(psmt!=null){psmt.close();}if(connection!=null){connection.close();}} catch (SQLException e) {e.printStackTrace();}}//    执行更新,返回影响行数   protected int executeUpdate(String sql,Object... params){Connection connection = null;PreparedStatement psmt = null;try {connection = getConnection();psmt = connection.prepareStatement(sql);if(params!=null && params.length>0){for (int i = 0; i < params.length; i++) {psmt.setObject(i+1,params[i]);}}return psmt.executeUpdate();} catch ( SQLException e) {e.printStackTrace();}finally {close(connection,psmt,null);}return 0;}}

然后让继承该抽象类

由于没有涉及到查询的改进,这里省去查询的方法。可以看到对于增删改方法,只需要调用父类的更新执行方法,传入sql语句和参数即可完成业务。

package com.zylai.fruit.dao.impl;import com.zylai.fruit.dao.FruitDAO;
import com.zylai.fruit.dao.base.BaseDAO;
import com.zylai.fruit.pojo.Fruit;import java.sql.*;
import java.util.ArrayList;
import java.util.List;/*** @Author: Zhao YunLai* @Date: 2022/06/21/18:05* @Description:*/
public class FruitDAOImpl extends BaseDAO implements FruitDAO {//    添加水果@Overridepublic boolean addFruit(Fruit fruit) {String sql = "insert into t_fruit values(0,?,?,?,?)";return super.executeUpdate(sql,fruit.getFname(),fruit.getPrice(),fruit.getFcount(),fruit.getRemark())>0;}//    更新水果库存信息@Overridepublic boolean updateFruit(Fruit fruit) {String sql = "update t_fruit set fname=?,price=?,fcount=?,remark=? where fid=?";return super.executeUpdate(sql,fruit.getFname(),fruit.getPrice(),fruit.getFcount(),fruit.getRemark(),fruit.getFid())>0;}//    删除水果@Overridepublic boolean deleteFruitByName(String fname) {String sql = "delete from t_fruit where fname=?";return super.executeUpdate(sql,fname)>0;}
}

2.5 改进:抽取查询操作 2.5.1 使用泛型

通过执行查询操作返回查询的结果集,然后通过get方法得到指定列的值赋值给实体类比如Fruit,而对于会有多个继承它,因此不能把实体类写死,所以采用泛型。

public abstract class BaseDAO<T> {}public class FruitDAOImpl extends BaseDAO<Fruit> implements FruitDAO {}

2.5.2 通过反射得到真正的类

因为泛型只是一个符号,我们不能直接通过泛型创建实例,所以用到反射技术来确定真正的实体类。

首先在类中定义Class 属性

//    T的Class对象private Class entityClass;

在的无参构造器中,确定真正的实体类

    public BaseDAO(){
//        getClass获取Class对象,当前我们执行的是new FruitDAOImpl(),创建的是FruitDAOImpl实例
//        那么构造方法首先会调用父类(BaseDAO)的无参构造方法
//        因此此处的getClass方法执行,获取的是FruitDAOImpl的Class
//        所以使用getGenericSuperclass
//        getGenericSuperclass是获取父类的泛型,即获取BaseDAO的泛型Type genericSuperclass = getClass().getGenericSuperclass();
//         ParameterizedType:参数化类型
//        获取(注意,泛型可以传递多个如,所以返回值是一个数组)中的T的真是类型Type[] actualTypeArguments = ((ParameterizedType) genericSuperclass).getActualTypeArguments();Type actualType = actualTypeArguments[0];try {entityClass = Class.forName(actualType.getTypeName());} catch (ClassNotFoundException e) {e.printStackTrace();}}

2.5.3 查询方法

在处理结果集时我们不知道T到底有几个参数,是什么类型。因此,我们需要获取结果集的元数据,所谓元数据就是描述结果集数据的数据,简单来讲,就是这个结果集有哪些列,什么类型等等。

之后通过Class对象创建一个T的实例进行操作即可

先定义一个工具函数:

//    通过反射,给obj对象的property属性赋propertyValue值private void setValue(Object obj,String property,Object propertyValue){Class clazz = obj.getClass();
//        获取property这个字符串对应的属性名,比如“fid”去找obj中的fid属性try {Field field  = clazz.getDeclaredField(property);if(field!=null){//            强制访问field.setAccessible(true);//为obj对象设置filed属性的值field.set(obj,propertyValue);}} catch (NoSuchFieldException | IllegalAccessException e) {e.printStackTrace();}}

执行查询的函数:

//    执行查询protected List<T> executeQuery(String sql,Object... params){List<T> list = new ArrayList<>();try {
//            获取连接connection = getConnection();
//            设置预处理对象psmt = connection.prepareStatement(sql);
//            设置参数setParams(psmt,params);
//            执行查询rs= psmt.executeQuery();
//            处理结果集
//            难点在于如何处理T,我们不知道T是什么类型并且不知道T有多少个参数
//            通过rs可以获取结果集的元数据
//            元数据:描述结果集数据的数据,简单讲,就是这个结果接有哪些列,什么类型等等ResultSetMetaData metaData = rs.getMetaData();int columnCount = metaData.getColumnCount();while(rs.next()){T entity = (T)entityClass.newInstance();for (int i = 0; i < columnCount; i++) {
//               获取列名String columnName = metaData.getColumnName(i + 1);
//                获取列对应的值Object columnValue = rs.getObject(i + 1);
//                    这里获取了列名,就需要根据列名把值填充给entity对象setValue(entity,columnName,columnValue);}list.add(entity);}} catch (SQLException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} finally {close(connection,psmt,rs);}return list;}

2.5.4 改进后的整个类为:

package com.zylai.fruit.dao.base;import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;/*** @Author: Zhao YunLai* @Date: 2022/06/21/21:45* @Description:*/
public abstract class BaseDAO<T> {protected final String DRIVER = "com.mysql.jdbc.Driver";protected final String URL = "jdbc:mysql://localhost:3306/fruitdb?useUnicode=true&characterEncoding=utf-8&useSSL=false";protected final String USER = "root";protected final String PWD = "root";//    老师是吧这几个放到这里的,我觉得可能会出现并发问题,不过目前不考虑这个protected Connection connection = null;protected PreparedStatement psmt = null;protected ResultSet rs = null;//    T的Class对象private Class entityClass;public BaseDAO(){
//        getClass获取Class对象,当前我们执行的是new FruitDAOImpl(),创建的是FruitDAOImpl实例
//        那么构造方法首先会调用父类(BaseDAO)的无参构造方法
//        因此此处的getClass方法执行,获取的是FruitDAOImpl的Class
//        所以使用getGenericSuperclass
//        getGenericSuperclass是获取父类的泛型,即获取BaseDAO的泛型Type genericSuperclass = getClass().getGenericSuperclass();
//         ParameterizedType:参数化类型
//        获取(注意,泛型可以传递多个如,所以返回值是一个数组)中的T的真是类型Type[] actualTypeArguments = ((ParameterizedType) genericSuperclass).getActualTypeArguments();Type actualType = actualTypeArguments[0];try {entityClass = Class.forName(actualType.getTypeName());} catch (ClassNotFoundException e) {e.printStackTrace();}}protected Connection getConnection(){try {//       1.加载驱动Class.forName(DRIVER);//            2.通过驱动获取连接对象return DriverManager.getConnection(URL, USER, PWD);} catch (ClassNotFoundException | SQLException e) {e.printStackTrace();}return null;}//    关闭资源protected  void close(Connection connection, PreparedStatement psmt, ResultSet rs){try {if(rs!=null){rs.close();}if(psmt!=null){psmt.close();}if(connection!=null){connection.close();}} catch (SQLException e) {e.printStackTrace();}}
//    给预处理对象设置参数private void setParams(PreparedStatement psmt,Object... params) throws SQLException {if(params!=null && params.length>0){for (int i = 0; i < params.length; i++) {psmt.setObject(i+1,params[i]);}}}//    通过反射,给obj对象的property属性赋propertyValue值private void setValue(Object obj,String property,Object propertyValue){Class clazz = obj.getClass();
//        获取property这个字符串对应的属性名,比如“fid”去找obj中的fid属性try {Field field  = clazz.getDeclaredField(property);if(field!=null){//            强制访问field.setAccessible(true);//为obj对象设置filed属性的值field.set(obj,propertyValue);}} catch (NoSuchFieldException | IllegalAccessException e) {e.printStackTrace();}}//    执行查询protected List<T> executeQuery(String sql,Object... params){List<T> list = new ArrayList<>();try {
//            获取连接connection = getConnection();
//            设置预处理对象psmt = connection.prepareStatement(sql);
//            设置参数setParams(psmt,params);
//            执行查询rs= psmt.executeQuery();
//            处理结果集
//            难点在于如何处理T,我们不知道T是什么类型并且不知道T有多少个参数
//            通过rs可以获取结果集的元数据
//            元数据:描述结果集数据的数据,简单讲,就是这个结果接有哪些列,什么类型等等ResultSetMetaData metaData = rs.getMetaData();int columnCount = metaData.getColumnCount();while(rs.next()){T entity = (T)entityClass.newInstance();for (int i = 0; i < columnCount; i++) {
//               获取列名String columnName = metaData.getColumnName(i + 1);
//                获取列对应的值Object columnValue = rs.getObject(i + 1);
//                    这里获取了列名,就需要根据列名把值填充给entity对象setValue(entity,columnName,columnValue);}list.add(entity);}} catch (SQLException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} finally {close(connection,psmt,rs);}return list;}//    通过查询返回一条记录protected T load(String sql,Object... params){try {
//            获取连接connection = getConnection();
//            设置预处理对象psmt = connection.prepareStatement(sql);
//            设置参数setParams(psmt,params);
//            执行查询rs= psmt.executeQuery();
//            处理结果集
//            难点在于如何处理T,我们不知道T是什么类型并且不知道T有多少个参数
//            通过rs可以获取结果集的元数据
//            元数据:描述结果集数据的数据,简单讲,就是这个结果接有哪些列,什么类型等等ResultSetMetaData metaData = rs.getMetaData();int columnCount = metaData.getColumnCount();if (rs.next()){T entity = (T)entityClass.newInstance();for (int i = 0; i < columnCount; i++) {
//               获取列名String columnName = metaData.getColumnName(i + 1);
//                获取列对应的值Object columnValue = rs.getObject(i + 1);
//                    这里获取了列名,就需要根据列名把值填充给entity对象setValue(entity,columnName,columnValue);}return entity;}} catch (SQLException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} finally {close(connection,psmt,rs);}return null;}//    执行复杂的查询,返回统计结果,这个方法暂时没有用到protected Object[] executeComplexQuery(String sql,Object... params){try {
//            获取连接connection = getConnection();
//            设置预处理对象psmt = connection.prepareStatement(sql);
//            设置参数setParams(psmt,params);
//            执行查询rs= psmt.executeQuery();
//            处理结果集
//            难点在于如何处理T,我们不知道T是什么类型并且不知道T有多少个参数
//            通过rs可以获取结果集的元数据
//            元数据:描述结果集数据的数据,简单讲,就是这个结果接有哪些列,什么类型等等ResultSetMetaData metaData = rs.getMetaData();int columnCount = metaData.getColumnCount();if (rs.next()){Object[] columnValueArr = new Object[columnCount];for (int i = 0; i < columnCount; i++) {
//                获取列对应的值Object columnValue = rs.getObject(i + 1);columnValueArr[i]=columnValue;}return columnValueArr;}} catch (SQLException e) {e.printStackTrace();} finally {close(connection,psmt,rs);}return null;}}

3 数据库连接池 3.1 概述和优势

之前的的连接都是用的时候才创建连接,当使用完成之后就销毁,这样效率很低,浪费资源。

使用数据库连接池就可以先创建多个连接对象放到连接池中,等到使用的时候直接从连接池中取出来,用完之后归还。

3.1.1 好处: 响应时间更快连接对象的利用率更高 3.1.2详细配置参数:

不需要背,只是参考,记住主要用的几个就可以了

配置缺省说明

name

配置这个属性的意义在于,如果存在多个数据源,监控的时候可以通过名字来区分开来。 如果没有配置,将会生成一个名字,格式是:”-” + .(this)

url

连接数据库的url,不同数据库不一样。例如:mysql : jdbc:mysql://10.20.153.104:3306/ : jdbc::thin:@10.20.149.85:1521:

连接数据库的用户名

连接数据库的密码。如果你不希望密码直接写在配置文件中,可以使用。详细看这里:使用

根据url自动识别 这一项可配可不配,如果不配置druid会根据url自动识别,然后选择相应的(建议配置下)

初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次时

最大连接池数量

已经不再使用,配置了也没效果

多层级笔记软件__笔记布局图

最小连接池数量

获取连接时最大等待时间,单位毫秒。配置了之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置属性为true使用非公平锁。

ts

false

是否缓存,也就是。对支持游标的数据库性能提升巨大,比如说。在mysql下建议关闭。

ments

-1

要启用,必须配置大于0,当大于0时,ts自动触发修改为true。在Druid中,不会存在下占用内存过多的问题,可以把这个数值配置大一些,比如说100

用来检测连接是否有效的sql,要求是一个查询语句。如果为null,、、都不会其作用。

true

申请连接时执行检测连接是否有效,做了这个配置会降低性能。

false

归还连接时执行检测连接是否有效,做了这个配置会降低性能

false

建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于,执行检测连接是否有效。

有两个含义: 1)线程会检测连接的间隔时间2)的判断依据,详细看属性的说明

un

不再使用,一个只支持一个

物理连接初始化的时候执行的sql

根据自动识别 当数据库抛出一些不可恢复的异常时,抛弃连接

属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有: 监控统计用的:stat日志用的:log4j防御sql注入的:wall

类型是List,如果同时配置了和,是组合关系,并非替换关系

3.2 使用 3.2.1 基础使用

将对应的jar引入到项目中之后,创建对象,进行连接即可。

注意:

被close的连接对象并没有真正关闭,而是将状态重新设置为空闲状态放回池子没有被close的连接对象会被一直占用,那么下次继续获取连接对象,是不会获取到这个对象的对于conn1对象指向别的东西,原始情况下jvm会回收conn1之前指向的对象,但是连接池中的连接默认有一个变量指着,所以不会被回收

package com.zylai.jdbc;import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidPooledConnection;import java.sql.Connection;
import java.sql.SQLException;/*** @Author: Zhao YunLai* @Date: 2022/06/22/14:13* @Description:*/
public class Demo02Druid {public static void main(String[] args) throws SQLException {DruidDataSource dataSource = new DruidDataSource();dataSource.setDriverClassName("com.mysql.jdbc.Driver");dataSource.setUrl("jdbc:mysql://localhost:3306/fruitdb?&useSSL=false&useUnicode=true&characterEncoding=utf-8");dataSource.setUsername("root");dataSource.setPassword("root");//        证明两点:
//        1.被close的连接对象并没有真正关闭,而是将状态重新设置为空闲状态放回池子
//        2.没有被close的连接对象会被一直占用,那么下次继续获取连接对象,是不会获取到这个对象的
//        对于conn1对象指向别的东西,原始情况下jvm会回收conn1之前指向的对象,但是连接池中的连接默认有一个变量指着,所以不会被回收for (int i = 0; i < 5; i++) {Connection conn1 = dataSource.getConnection();Connection conn2 = dataSource.getConnection();System.out.println(conn1);System.out.println(conn2);if(i%3==0){conn1.close();conn2.close();}}}}

3.2.2 验证连接池的部分参数

这里以最大连接数和最长等待时间为例,当获取完所有的连接之后,再去获取连接超过指定的等待时间会抛出异常

package com.zylai.jdbc;import com.alibaba.druid.pool.DruidDataSource;import java.sql.Connection;
import java.sql.SQLException;/*** @Author: Zhao YunLai* @Date: 2022/06/22/14:13* @Description: 验证连接池的部分参数*/
public class Demo03Druid {public static void main(String[] args) throws SQLException {DruidDataSource dataSource = new DruidDataSource();dataSource.setDriverClassName("com.mysql.jdbc.Driver");dataSource.setUrl("jdbc:mysql://localhost:3306/fruitdb?&useSSL=false&useUnicode=true&characterEncoding=utf-8");dataSource.setUsername("root");dataSource.setPassword("root");dataSource.setInitialSize(2);dataSource.setMaxActive(5);dataSource.setMaxWait(5000);for (int i = 0; i < 10; i++) {Connection conn1 = dataSource.getConnection();System.out.println(i+" "+conn1);}}}

3.2.3 读取配置文件信息创建连接池

在实际开发中,比如数据库url,密码用户等信息都是写在配置文件中,不会直接写在代码中进行硬编码

package com.zylai.jdbc;import com.alibaba.druid.pool.DruidDataSource;import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;/*** @Author: Zhao YunLai* @Date: 2022/06/22/14:13* @Description: 从配置文件中读取信息*/
public class Demo04Druid {public static void main(String[] args) throws SQLException, IOException {Properties properties = new Properties();InputStream is = Demo04Druid.class.getClassLoader().getResourceAsStream("jdbc.properties");properties.load(is);DruidDataSource dataSource = new DruidDataSource();dataSource.setDriverClassName(properties.getProperty("jdbc.driverClassName"));dataSource.setUrl(properties.getProperty("jdbc.url"));dataSource.setUsername(properties.getProperty("jdbc.username"));dataSource.setPassword(properties.getProperty("jdbc.pwd"));dataSource.setInitialSize(Integer.parseInt(properties.getProperty("druid.initialSize")));dataSource.setMaxActive(Integer.parseInt(properties.getProperty("druid.maxActive")));dataSource.setMaxWait(Long.parseLong(properties.getProperty("druid.maxWait")));for (int i = 0; i < 10; i++) {Connection conn1 = dataSource.getConnection();System.out.println(i+" "+conn1);}}}

配置文件jdbc.:

jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/fruitdb?&useSSL=false&useUnicode=true&characterEncoding=utf-8
jdbc.username=root
jdbc.pwd=rootdruid.initialSize=2
druid.maxActive=5
druid.maxWait=5000

3.2.4 直接通过配置文件建立连接池

通过ry.();返回**接口**,直接通过配置文件建立好了连接池。不过这里对于配置文件的格式有要求。

package com.zylai.jdbc;import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;/*** @Author: Zhao YunLai* @Date: 2022/06/22/14:13* @Description: 直接通过配置文件创建连接池*/
public class Demo05Druid {public static void main(String[] args) throws Exception {Properties properties = new Properties();InputStream is = Demo05Druid.class.getClassLoader().getResourceAsStream("jdbc2.properties");properties.load(is);DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);for (int i = 0; i < 10; i++) {Connection conn1 = dataSource.getConnection();System.out.println(i+" "+conn1);}}
}

配置文件的格式应该一致

driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/fruitdb?&useSSL=false&useUnicode=true&characterEncoding=utf-8
username=root
password=rootinitialSize=2
maxActive=5
maxWait=5000

关于我们

最火推荐

小编推荐

联系我们


版权声明:本站内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 88@qq.com 举报,一经查实,本站将立刻删除。备案号:桂ICP备2021009421号
Powered By Z-BlogPHP.
复制成功
微信号:
我知道了