• 使用 jdbc 技术升级水果库存系统(优化版本)


    • 抽取执行更新方法
    • 抽取查询方法 —— ResultSetMetaData  
      ResultSetMetaData rsmd = rs.getMetaData();//元数据,结果集的结构数据
    • 抽取查询方法 —— 解析结果集封装成实体对象
    • 提取 获取连接释放资源 的方法
    • 将数据库配置信息转移到配置文件
    1. <dependencies>
    2. <dependency>
    3. <groupId>org.projectlombokgroupId>
    4. <artifactId>lombokartifactId>
    5. <version>1.18.10version>
    6. dependency>
    7. <dependency>
    8. <groupId>junitgroupId>
    9. <artifactId>junitartifactId>
    10. <version>4.12version>
    11. <scope>testscope>
    12. dependency>
    13. <dependency>
    14. <groupId>mysqlgroupId>
    15. <artifactId>mysql-connector-javaartifactId>
    16. <version>8.0.28version>
    17. dependency>
    18. dependencies>
    1. package com.csdn.fruit.pojo;
    2. import lombok.AllArgsConstructor;
    3. import lombok.Data;
    4. import lombok.NoArgsConstructor;
    5. import java.io.Serializable;
    6. @Data
    7. @NoArgsConstructor
    8. @AllArgsConstructor
    9. public class Fruit implements Serializable {
    10. private Integer fid;
    11. private String fname;
    12. private Integer price;
    13. private Integer fcount;
    14. private String remark;
    15. public Fruit(String fname, Integer price, Integer fcount, String remark) {
    16. this.fname = fname;
    17. this.price = price;
    18. this.fcount = fcount;
    19. this.remark = remark;
    20. }
    21. @Override
    22. public String toString() {
    23. return fname + "\t\t" + price + "\t\t" + fcount + "\t\t" + remark;
    24. }
    25. }
    •  
    1. jdbc.driver=com.mysql.cj.jdbc.Driver
    2. jdbc.url=jdbc:mysql:///fruitdb
    3. jdbc.user=root
    4. jdbc.pwd=123456
    1. package com.csdn.mymvc.dao;
    2. import com.csdn.mymvc.util.ClassUtil;
    3. import java.io.IOException;
    4. import java.io.InputStream;
    5. import java.lang.reflect.ParameterizedType;
    6. import java.lang.reflect.Type;
    7. import java.sql.*;
    8. import java.util.ArrayList;
    9. import java.util.List;
    10. import java.util.Properties;
    11. public abstract class BaseDao {
    12. private String DRIVER;
    13. private String URL;
    14. private String USER;
    15. private String PWD;
    16. private String entityClassName;
    17. public BaseDao() {
    18. // this 是谁? this代表的是 FruitDaoImpl 的实例对象,因为 BaseDao是抽象类,不能直接创建对象,所以 new 的是它的子类对象 FruitDaoImpl
    19. // this.getClass() 获取的是 FruitDaoImpl 的Class对象
    20. // getGenericSuperclass() 获取到的是:BaseDao
    21. // Type 是顶层接口,表示所有的类型。它有一个子接口:ParameterizedType
    22. ParameterizedType genericSuperclass = (ParameterizedType) this.getClass().getGenericSuperclass();
    23. // Actual:实际的
    24. // getActualTypeArguments() 获取实际的类型参数
    25. Type[] actualTypeArguments = genericSuperclass.getActualTypeArguments();
    26. Type actualTypeArgument = actualTypeArguments[0];
    27. // System.out.println(actualTypeArgument.getTypeName());//com.csdn.fruit.pojo.Fruit
    28. entityClassName = actualTypeArgument.getTypeName();
    29. loadJdbcProperties();
    30. }
    31. //加载jdbc.properties文件
    32. private void loadJdbcProperties() {
    33. try {
    34. InputStream inputStream = getClass().getClassLoader().getResourceAsStream("jdbc.properties");
    35. Properties properties = new Properties();
    36. properties.load(inputStream);
    37. DRIVER = properties.getProperty("jdbc.driver", "com.mysql.cj.jdbc.Driver");
    38. URL = properties.getProperty("jdbc.url", "jdbc:mysql:///fruitdb");
    39. USER = properties.getProperty("jdbc.user", "root");
    40. PWD = properties.getProperty("jdbc.pwd", "123456");
    41. } catch (IOException e) {
    42. throw new RuntimeException(e);
    43. }
    44. }
    45. private Connection getConn() {
    46. try {
    47. Class.forName(DRIVER);
    48. return DriverManager.getConnection(URL, USER, PWD);
    49. } catch (ClassNotFoundException | SQLException e) {
    50. throw new RuntimeException(e);
    51. }
    52. }
    53. private void close(Connection conn, PreparedStatement psmt, ResultSet rs) {
    54. try {
    55. if (rs != null) {
    56. rs.close();
    57. }
    58. if (psmt != null) {
    59. psmt.close();
    60. }
    61. if (conn != null && !conn.isClosed()) {
    62. conn.close();
    63. }
    64. } catch (SQLException e) {
    65. throw new RuntimeException(e);
    66. }
    67. }
    68. //抽取执行更新方法
    69. //执行更新,返回影响行数
    70. protected int executeUpdate(String sql, Object... params) {
    71. PreparedStatement psmt = null;
    72. Connection conn = null;
    73. try {
    74. conn = getConn();
    75. psmt = conn.prepareStatement(sql);
    76. setParams(psmt, params);
    77. return psmt.executeUpdate();
    78. } catch (SQLException e) {
    79. throw new RuntimeException(e);
    80. } finally {
    81. close(conn, psmt, null);
    82. }
    83. }
    84. //设置参数
    85. private void setParams(PreparedStatement psmt, Object... params) throws SQLException {
    86. if (params != null && params.length > 0) {
    87. for (int i = 0; i < params.length; i++) {
    88. psmt.setObject(i + 1, params[i]);
    89. }
    90. }
    91. }
    92. //执行查询,返回集合
    93. protected List executeQuery(String sql, Object... params) {
    94. List list = new ArrayList<>();
    95. Connection conn = null;
    96. PreparedStatement psmt = null;
    97. ResultSet rs = null;
    98. try {
    99. conn = getConn();
    100. psmt = conn.prepareStatement(sql);
    101. setParams(psmt, params);
    102. rs = psmt.executeQuery();
    103. ResultSetMetaData rsmd = rs.getMetaData();//元数据,结果集的结构数据
    104. while (rs.next()) {
    105. //T t = new T(); T仅仅是一个符号,所以不能 new
    106. T t = (T) ClassUtil.createInstance(entityClassName);
    107. int columnCount = rsmd.getColumnCount();//获取结果集的列的数据
    108. //jdbc中都是从 1 开始,所以要把 i 改成 从 1 开始
    109. for (int i = 1; i <= columnCount; i++) {
    110. //假设循环 5 次,得到 5 个值,应该对应的是一个对象的 5 个属性的值
    111. String columnName = rsmd.getColumnLabel(i);
    112. Object columnValue = rs.getObject(i);
    113. //给 t 这个对象的 columnName 属性赋 columnValue 值
    114. ClassUtil.setProperty(t, columnName, columnValue);
    115. }
    116. list.add(t);
    117. }
    118. return list;
    119. } catch (SQLException e) {
    120. throw new RuntimeException(e);
    121. } finally {
    122. close(conn, psmt, rs);
    123. }
    124. }
    125. protected T load(String sql, Object... params) {
    126. Connection conn = null;
    127. PreparedStatement psmt = null;
    128. ResultSet rs = null;
    129. try {
    130. conn = getConn();
    131. psmt = conn.prepareStatement(sql);
    132. setParams(psmt, params);
    133. rs = psmt.executeQuery();
    134. ResultSetMetaData rsmd = rs.getMetaData();//元数据,结果集的结构数据
    135. if (rs.next()) {
    136. //T t = new T(); T仅仅是一个符号,所以不能 new
    137. T t = (T) ClassUtil.createInstance(entityClassName);
    138. int columnCount = rsmd.getColumnCount();//获取结果集的列的数据
    139. //jdbc中都是从 1 开始,所以要把 i 改成 从 1 开始
    140. for (int i = 1; i <= columnCount; i++) {
    141. //假设循环 5 次,得到 5 个值,应该对应的是一个对象的 5 个属性的值
    142. String columnName = rsmd.getColumnLabel(i);
    143. Object columnValue = rs.getObject(i);
    144. //给 t 这个对象的 columnName 属性赋 columnValue 值
    145. ClassUtil.setProperty(t, columnName, columnValue);
    146. }
    147. return t;
    148. }
    149. } catch (SQLException e) {
    150. throw new RuntimeException(e);
    151. } finally {
    152. close(conn, psmt, rs);
    153. }
    154. return null;
    155. }
    156. }
    1. package com.csdn.mymvc.util;
    2. import java.lang.reflect.Field;
    3. import java.lang.reflect.InvocationTargetException;
    4. public class ClassUtil {
    5. public static Object createInstance(String entityClassName) {
    6. try {
    7. return Class.forName(entityClassName).getDeclaredConstructor().newInstance();
    8. } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException |
    9. ClassNotFoundException e) {
    10. throw new RuntimeException(e);
    11. }
    12. }
    13. public static void setProperty(Object instance, String propertyName, Object propertyValue) {
    14. Class aClass = instance.getClass();
    15. try {
    16. Field field = aClass.getDeclaredField(propertyName);
    17. field.setAccessible(true);
    18. field.set(instance, propertyValue);
    19. } catch (NoSuchFieldException | IllegalAccessException e) {
    20. throw new RuntimeException(e);
    21. }
    22. }
    23. }

    1. package com.csdn.fruit.dao;
    2. import com.csdn.fruit.pojo.Fruit;
    3. import java.util.List;
    4. //dao :Data Access Object 数据访问对象
    5. //接口设计
    6. public interface FruitDao {
    7. void addFruit(Fruit fruit);
    8. void delFruit(String fname);
    9. void updateFruit(Fruit fruit);
    10. List getFruitList();
    11. Fruit getFruitByFname(String fname);
    12. }
    1. package com.csdn.fruit.dao.impl;
    2. import com.csdn.fruit.dao.FruitDao;
    3. import com.csdn.fruit.pojo.Fruit;
    4. import com.csdn.mymvc.dao.BaseDao;
    5. import java.util.List;
    6. public class FruitDaoImpl extends BaseDao implements FruitDao {
    7. @Override
    8. public void addFruit(Fruit fruit) {
    9. String sql = "insert into t_fruit values (0,?,?,?,?)";
    10. super.executeUpdate(sql, fruit.getFname(), fruit.getPrice(), fruit.getFcount(), fruit.getRemark());
    11. }
    12. @Override
    13. public void delFruit(String fname) {
    14. String sql = "delete from t_fruit where fname=?";
    15. super.executeUpdate(sql, fname);
    16. }
    17. @Override
    18. public void updateFruit(Fruit fruit) {
    19. String sql = "update t_fruit set fcount=? where fname = ?";
    20. super.executeUpdate(sql, fruit.getFcount(), fruit.getFname());
    21. }
    22. @Override
    23. public List getFruitList() {
    24. return super.executeQuery("select * from t_fruit");
    25. }
    26. @Override
    27. public Fruit getFruitByFname(String fname) {
    28. return load("select * from t_fruit where fname = ?", fname);
    29. }
    30. }
    1. package com.csdn.fruit.view;
    2. import com.csdn.fruit.dao.FruitDao;
    3. import com.csdn.fruit.dao.impl.FruitDaoImpl;
    4. import com.csdn.fruit.pojo.Fruit;
    5. import java.util.List;
    6. import java.util.Scanner;
    7. public class Menu {
    8. Scanner input = new Scanner(System.in);
    9. private FruitDao fruitDao = new FruitDaoImpl();
    10. //显示主菜单
    11. public int showMainMenu() {
    12. System.out.println("================欢迎使用水果库存系统===================");
    13. System.out.println("1.显示库存列表");
    14. System.out.println("2.添加库存记录");
    15. System.out.println("3.查看特定库存");
    16. System.out.println("4.水果下架");
    17. System.out.println("5.退出");
    18. System.out.println("====================================================");
    19. System.out.print("请选择:");
    20. return input.nextInt();
    21. }
    22. //显示库存列表
    23. public void showFruitList() {
    24. List fruitList = fruitDao.getFruitList();
    25. System.out.println("----------------------------------------------------");
    26. System.out.println("名称\t\t单价\t\t库存\t\t备注");
    27. if (fruitList == null || fruitList.size() <= 0) {
    28. System.out.println("对不起,库存为空!");
    29. } else {
    30. /* fruitList.forEach(new Consumer() {
    31. @Override
    32. public void accept(Fruit fruit) {
    33. System.out.println(fruit);
    34. }
    35. });*/
    36. // fruitList.forEach(fruit -> System.out.println(fruit));
    37. fruitList.forEach(System.out::println);
    38. }
    39. System.out.println("----------------------------------------------------");
    40. }
    41. //添加库存记录
    42. public void addFruit() {
    43. System.out.print("请输入水果名称:");
    44. String fname = input.next();
    45. Fruit fruit = fruitDao.getFruitByFname(fname);
    46. if (fruit == null) {
    47. System.out.print("请输入水果单价:");
    48. Integer price = input.nextInt();
    49. System.out.print("请输入水果库存:");
    50. Integer fcount = input.nextInt();
    51. System.out.print("请输入水果备注:");
    52. String remark = input.next();
    53. fruit = new Fruit(fname, price, fcount, remark);
    54. fruitDao.addFruit(fruit);
    55. } else {
    56. System.out.print("请输入追加的库存量:");
    57. Integer fcount = input.nextInt();
    58. fruit.setFcount(fruit.getFcount() + fcount);
    59. fruitDao.updateFruit(fruit);
    60. }
    61. System.out.println("添加成功!");
    62. }
    63. //查看特定库存记录
    64. public void showFruitInfo() {
    65. System.out.print("请输入水果名称:");
    66. String fname = input.next();
    67. Fruit fruit = fruitDao.getFruitByFname(fname);
    68. if (fruit == null) {
    69. System.out.println("对不起,没有找到对应的库存记录!");
    70. } else {
    71. System.out.println("----------------------------------------------------");
    72. System.out.println("名称\t\t单价\t\t库存\t\t备注");
    73. System.out.println(fruit);
    74. System.out.println("----------------------------------------------------");
    75. }
    76. }
    77. //水果下架
    78. public void delFruit() {
    79. System.out.print("请输入水果名称:");
    80. String fname = input.next();
    81. Fruit fruit = fruitDao.getFruitByFname(fname);
    82. if (fruit == null) {
    83. System.out.println("对不起,没有找到需要下架的库存记录!");
    84. } else {
    85. System.out.print("是否确认下架?(Y/N)");
    86. String confirm = input.next();
    87. if ("y".equalsIgnoreCase(confirm)) {
    88. fruitDao.delFruit(fname);
    89. }
    90. }
    91. }
    92. //退出
    93. public boolean exit() {
    94. System.out.print("是否确认退出?(Y/N)");
    95. String confirm = input.next();
    96. boolean flag= !"y".equalsIgnoreCase(confirm);
    97. return flag;
    98. }
    99. }
    1. package com.csdn.fruit.view;
    2. public class Client {
    3. public static void main(String[] args) {
    4. Menu m = new Menu();
    5. boolean flag = true;
    6. while (flag) {
    7. int slt = m.showMainMenu();
    8. switch (slt) {
    9. case 1:
    10. m.showFruitList();
    11. break;
    12. case 2:
    13. m.addFruit();
    14. break;
    15. case 3:
    16. m.showFruitInfo();
    17. break;
    18. case 4:
    19. m.delFruit();
    20. break;
    21. case 5:
    22. //方法设计时是否需要返回值,依据是:是否在调用的地方需要留下一些值用于再运算
    23. flag = m.exit();
    24. break;
    25. default:
    26. System.out.println("你不按套路出牌!");
    27. break;
    28. }
    29. }
    30. System.out.println("谢谢使用!再见!");
    31. }
    32. }
    1. package com.csdn.dao.impl;
    2. import com.csdn.fruit.dao.FruitDao;
    3. import com.csdn.fruit.dao.impl.FruitDaoImpl;
    4. import com.csdn.fruit.pojo.Fruit;
    5. import org.junit.Test;
    6. import java.util.List;
    7. public class FruitDaoImplTest {
    8. private FruitDao fruitDao = new FruitDaoImpl();
    9. @Test
    10. public void testAddFruit() {
    11. Fruit fruit = new Fruit("香蕉", 7, 77, "波罗蜜是一种神奇的水果!");
    12. fruitDao.addFruit(fruit);
    13. }
    14. @Test
    15. public void testDelFruit() {
    16. fruitDao.delFruit("哈密瓜");
    17. }
    18. @Test
    19. public void testUpdateFruit() {
    20. Fruit fruit = new Fruit("波罗蜜", 5, 1000, "好吃");
    21. fruitDao.updateFruit(fruit);
    22. }
    23. @Test
    24. public void testGetFruitList() {
    25. List fruitList = fruitDao.getFruitList();
    26. fruitList.stream().forEach(System.out::println);
    27. }
    28. @Test
    29. public void testGetFruitByFname() {
    30. Fruit fruit = fruitDao.getFruitByFname("波罗蜜");
    31. System.out.println(fruit);
    32. }
    33. /*
    34. // this 是谁? this代表的是 FruitDaoImpl 的实例对象,因为 BaseDao是抽象类,不能直接创建对象,所以 new 的是它的子类对象 FruitDaoImpl
    35. // this.getClass() 获取的是 FruitDaoImpl 的Class对象
    36. // getGenericSuperclass() 获取到的是:BaseDao
    37. // Type 是顶层接口,表示所有的类型。它有一个子接口:ParameterizedType
    38. ParameterizedType genericSuperclass = (ParameterizedType) this.getClass().getGenericSuperclass();
    39. // Actual:实际的
    40. // getActualTypeArguments() 获取实际的类型参数
    41. Type[] actualTypeArguments = genericSuperclass.getActualTypeArguments();
    42. Type actualTypeArgument = actualTypeArguments[0];
    43. // System.out.println(actualTypeArgument.getTypeName());//com.csdn.fruit.pojo.Fruit
    44. entityClassName = actualTypeArgument.getTypeName();
    45. loadJdbcProperties();
    46. */
    47. @Test
    48. public void testActualTypeArgument() {
    49. //这个方法是用来测试 actualTypeArgument 实际返回的参数
    50. }
    51. }
  • 相关阅读:
    CSS常用背景属性
    【MYSQL】在线恢复主从复制方案
    算法的时间复杂度和空间复杂度
    2022年10月29日 20点 程序爱生活 纳指和恒指可能会小破前高,然后有概率继续破新低,但是破完就需要考虑一次较大级别反弹的概率了! 第一次不再完全看空!
    Maven多环境下 active: @profileActive@报错问题解决
    网络文件传输程序设计(上)
    构建自定义ChatGPT,微软推出Copilot Studio
    【网络容灾失败案例】三种HTTP请求重试导致重复提交
    信息系统安全运维和管理指南
    Docker 及 Docker Compose 安装指南
  • 原文地址:https://blog.csdn.net/m0_65152767/article/details/134043216