java练习:图书销售管理系统(二),开发前的准备工作

简介

在上一篇中详细介绍了关系型数据表的一些设计(很浅显),现在来开发前的准备工作
在这里插入图片描述

准备阶段

分为四个阶段(分散思想还真不好搞,要我自己肯定一个类写到底)……
在这里插入图片描述
在这里先准备个包main,存放准备工作的类

编写数据表实体类

设计好了数据表(上一篇博客中),接下来就要为每张数据表各设立一个实体类了(这很OOP),方便在程序中直接操作数据表内容(最好写在同个包下)。

首先考虑设立一个父类,作为数据表的基类,由于每个表中都有主键列id,所以将id放入该类中(共有变量),然后发展其他类
这里以为book表为例

package main;

package database;

public class Book extends ValueObject {
    //名称,简介,加个,种类W,出版社W,图片url,作者,库存
    private String book_name;
    private String book_intro;
    private String book_price;
    private String type_id_fk;
    private String pub_id_fk;
    private String image_url;
    private String author;
    private String repertory_size;
   //省略setter和getter方法(我省略写在博客上可不是你让你省略……)
}  

可以看到,

  • 名称与数据表中字段名称一样,方便操作
  • 不论数据表中的类型是什么,实体类中都是String,方便数据读取,需要该类特质的时候,再利用javaAPI转换为相应的类型。其他的表也是如此 。

读取配置文件

先写好jdbc配置文件(我的配置文件名jdbc.properties),然后写个工具类解决问题,代码如下

package main;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public class PropsUtil {

    private static Properties props = new Properties();
    private static String fileName = "jdbc.properties";
    private static InputStream is;
    private static String j_driver;
    private static String j_url;
    private static String j_user;
    private static String j_pass;

    static {
        try {
            is = new FileInputStream(fileName);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        try {
            props.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        }
        j_driver = props.getProperty("jdbc.driver");
        j_url = props.getProperty("jdbc.url");
        j_user = props.getProperty("jdbc.user");
        j_pass = props.getProperty("jdbc.pass");
    }
}

很简单,就是加载文件并读取属性,不再过多解释

jdbc操作类

主要作用是执行sql语句,代码如下

package main;

import java.sql.*;

//执行jdbc操作
public class JDBCExector{

    private static String driver = PropsUtil.j_driver;
    private static String url = PropsUtil.j_url;
    private static String user = PropsUtil.j_user;
    private static String pass = PropsUtil.j_pass;
    private Connection conn;
    //单例类维护conn(创建conn成本巨大)
    private static JDBCExector jdbcExector;
    private Statement stmt;

    private JDBCExector() throws Exception {
        Class.forName(driver);
        conn = DriverManager.getConnection(url,user,pass);
        stmt = conn.createStatement();
    }
    public static JDBCExector getInstance() throws Exception {
        if (jdbcExector == null){
            return new JDBCExector();
        }
        return jdbcExector;
    }

    public ResultSet executeQuery(String sql) throws SQLException {
        ResultSet result = stmt.executeQuery(sql);
        return result;
    }
    public int executeUpdate(String sql) throws SQLException {
        int result = -1;
        //必须指定该属性才能得到主键
        stmt.executeUpdate(sql,Statement.RETURN_GENERATED_KEYS);
        //得到主键
        ResultSet primarys = stmt.getGeneratedKeys();
        while(primarys.next()){
            result = primarys.getInt(1);
        }
        return result;
    }
}

  • 定义连接属性,由PropsUtil(刚写好的配置文件工具类)中的静态变量为这些变量赋值
  • 由于Connection创建成本巨大,所以使用单例类来维护,构造器私有(无法在构造器中初始化属性),只能通过自身newInstance方法获得对象,ConnectionStatement在构造器中就初始化
  • 接下来就是执行sql语句,查询返回结果集,或者操作返回被影响的条数(插入操作返回主键,方便对插入的对象进行操作),尤其记得返回主键必须为executeUpdate指定Statement.RETURN_GENERATED_KEYS属性

数据转换工具类

目的是将结果集包装为Collection集合(方便对元素进行操作……),代码如下

package main;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.ResultSet;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

/**
 * 数据转换类
 * 为数据表对应的数据赋值
 */
public class DataUtil {

	//主操作
    public Collection getData(Collection result, ResultSet rs,Class clazz)
            throws Exception {
        while(rs.next()){
            //新建数据表实体类对象
            Object ov = clazz.getDeclaredConstructor().newInstance();
            //得到所有成员变量
            Field[] fields = clazz.getDeclaredFields();
            Field[] superFields = clazz.getSuperclass().getDeclaredFields();
            Field[] allFields = addFields(fields,superFields);
            for (Field f : allFields){
                Method setterMethod = clazz.getMethod(getSetterMethodName(f.getName()),
                        f.getType());
                invokeMethod(rs,f,ov,setterMethod);
            }
            result.add(ov);
        }
        return  result;
    }


    //从ResultSet中获取一个字段的数据,并调用ov的setter方法设置变量值
    //参数(结果集/数据,变量/变量名称,对象/主调,方法/setter)
    private static void invokeMethod(ResultSet rs,Field field,
                     Object ov,Method setterMethod) throws Exception {
        //数据
        String value = rs.getString(field.getName());
        setterMethod.invoke(ov,value);
    }
    //得到成员变量的Setter方法名
    private static String getSetterMethodName(String fieldName){
        //首字母大写
        String begin = fieldName.substring(0,1).toUpperCase();
        String end = fieldName.substring(1);
        String result = "set" + begin + end;
        return result;
    }

    //数组相加的方法
    private static Field[] addFields(Field[] f1,Field[] f2){
        List<Field> l = new LinkedList<>();
        Collections.addAll(l, f1);
        Collections.addAll(l, f2);
        return l.toArray(new Field[f1.length + f2.length]);
    }
}

这里利用的是反射的方法,获取实体类的对象成员变量方法,并根据数据表赋值

  • 既然是包装,传入参数肯定是集合结果集,因为要得到结果集对应的实体类信息,所以还需要传入该实体类的类别
  • getData方法:逐行读取结果集,新建实体类对象,得到所有成员变量(包括父类,这里写了一个数组相加的方法addFields),然后迭代成员变量,得到每个成员变量setter方法(getSetterMethodName方法),最后得到该字段的内容,执行ov对象的setter方法赋值
  • 设计方法时要充分考虑方法可能用到的信息,并设计相应的参数列表

小结

反射部分掌握不太熟练(clazz类和实例有点混乱,而且参数列表太乱了……),设计也不太模块化,写这么浅显易懂的东西我又使用了一个多小时(编程5分钟,扯淡两小时),得加快一下学习进度了

猜你喜欢

转载自blog.csdn.net/weixin_43560566/article/details/88901284
今日推荐