MySQL高级 JDBC

MySQL高级

1. 通用查询方法实现
1.1 技术点分析
需求:
	用户提供SQL语句,并且提供对应SQL语句的使用参数,同时告知方法需要返回数据类型是哪一个类型。

举例说明:
	1. table Student ==> SQL, Parameter, 【告知当前查询数据类型为Student类型】
	2. table Employee ==> SQL, Parameter, 【查询结果应该也是一个Employee对象】
	3. table Worker ==> SQL, Parameter, 【查询结果应该也是一个Worker对象】 
	
	ORM ==> Object-relational mapping (ORM, O/RM, and O/R mapping tool
		对象关系映射 对象 <==> 数据表 映射关系
	
	数据库相关框架:
		MyBatis Hibernate MyBatis-Plus DbUtils

缺少的技术点:
	是否存在泛型期望???
		存在,代码需要支持任意数据类型,但是要求数据类型一致化。
	是否存在对Class的需求???
		需要Class提供当前数据类型,并且可以利用反射支持类对象创建,成员变量赋值取值
	是否需要统一操作获取查询结果集字段名称???
		需要获取字段名称,映射类对象成员变量名字
	赋值操作,类型转换??? Object ==> 指定类型	
		提供第三方工具类,辅助完成任意类型赋值成员变量操作过程。
1.2 泛型和Class对象解决数据类型问题
package com.qfedu.a_utilTest;

import java.lang.reflect.InvocationTargetException;

public class Demo1 {
    public static void main(String[] args) {
        Class<Student> studentClass = Student.class;

        Student student = getInstance(studentClass);
        String s = getInstance(String.class);
        Demo1 demo1 = getInstance(Demo1.class);
        SingleDog singleDog = getInstance(SingleDog.class);
    }

    public static <T> T getInstance(Class<T> cls) {
        T t = null;
        try {
            t = cls.getConstructor().newInstance();
        } catch (InstantiationException | IllegalAccessException
                | InvocationTargetException | NoSuchMethodException e) {
            e.printStackTrace();
        }

        return t;
    }
}
1.3 赋值问题解决 BeanUtils工具类
package com.qfedu.a_utilTest;

import org.apache.commons.beanutils.BeanUtils;

import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;

/**
 * BeanUtils功能演示
 */
public class Demo2 {
    public static void main(String[] args)
            throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
        Student student = new Student();

        System.out.println(student);

        /*
        public static void setProperty(Object bean, String name, Object value)
        给予指定JavaBean规范类对象,指定成员变量赋值操作。
            bean 要求是一个符合JavaBean规范的类对象。
            name 指定字段名称
            value 对应当前字段赋值使用都是数据,Object类型
         */
        BeanUtils.setProperty(student, "id", "10");
        BeanUtils.setProperty(student, "age", -106);
        BeanUtils.setProperty(student, "name", "张三");
        BeanUtils.setProperty(student, "gender", "false");
        BeanUtils.setProperty(student, "score", "100");
        BeanUtils.setProperty(student, "info", "XXXXXXXXXXXXXXXXX");

        System.out.println(student);

        /*
        public static String getProperty(Object bean, String name);
            返回指定JavaBean规范类对象指定成员变量的数据。数据返回情况为String类型数据。保证数据完整性
            bean 要求是一个符合JavaBean规范的类对象。
            name 指定字段名称
         */
        System.out.println(BeanUtils.getProperty(student, "id"));
        System.out.println(BeanUtils.getProperty(student, "name"));
        System.out.println(BeanUtils.getProperty(student, "age"));
        System.out.println(BeanUtils.getProperty(student, "gender"));
        System.out.println(BeanUtils.getProperty(student, "score"));
        System.out.println(BeanUtils.getProperty(student, "info"));

        System.out.println();

        Student student1 = new Student();
        System.out.println(student1);
        /*
        public static void copyProperties(Object dest, Object orig);
        从源数据对象中,赋值成员变量数据,到目标对象中
            dest 目标对象要求符合JavaBean规范
            orig 源数据对象,要求符合JavaBean规范
         */
        BeanUtils.copyProperties(student1, student);
        System.out.println(student1);
        System.out.println(student.hashCode());
        System.out.println(student1.hashCode());

        /*
        int compare(T o1, T o2); Comparator<T>
        System.out.println((int) ((15.01 - 15.02) * 100)); ==> 0
        System.out.println(15.01);
        System.out.println(15.02);
        */


        HashMap<String, String> map = new HashMap<>();
        /*
        id=1&name=彭于晏&age=16&gender=0&info=无敌帅气&score=100
        ==> URL 请求参数 ==> Java代码中 转换为 一个Map对象 ==> 指定类对象。

        Servlet中
         */
        map.put("id", "290");
        map.put("name", "李四");
//        map.put("age", "-18");
        map.put("gender", "false");
        map.put("score", "59");
        map.put("info", "菜的一批~~~");
        map.put("married", "false");
        /*
        public static void populate(Object bean, Map properties)
         */
        Student student2 = new Student();
        BeanUtils.populate(student2, map);
        System.out.println(student2);
    }
}
1.4 查询结果集字段名获取
package com.qfedu.a_utilTest;

import util.JdbcUtil;

import java.sql.*;

/**
 * 结果集数据分析,解析
 */
public class Demo3 {
    public static void main(String[] args) throws SQLException {
        Connection connection = JdbcUtil.getConnection();

        String sql = "select * from student_info.student where id = 1";

        PreparedStatement statement = connection.prepareStatement(sql);

        ResultSet resultSet = statement.executeQuery();

        // 结果集元数据
        ResultSetMetaData metaData = resultSet.getMetaData();

        // 可以获取当前查询结果集中的结果字段个数/列数
        int columnCount = metaData.getColumnCount();
        System.out.println(columnCount);

        // 根据总列数获取每一列对应的字段名称
        System.out.println(metaData.getColumnName(1));
        System.out.println(metaData.getColumnName(2));
        System.out.println(metaData.getColumnName(3));
        System.out.println(metaData.getColumnName(4));
        System.out.println(metaData.getColumnName(5)) ;
        System.out.println(metaData.getColumnName(6));

        if (resultSet.next()) {
            // 根据字段名称获取对应字段对应数据
            System.out.println(resultSet.getObject(metaData.getColumnName(1)));
            System.out.println(resultSet.getObject(metaData.getColumnName(2)));
            System.out.println(resultSet.getObject(metaData.getColumnName(3)));
            System.out.println(resultSet.getObject(metaData.getColumnName(4)));
            System.out.println(resultSet.getObject(metaData.getColumnName(5)));
            System.out.println(resultSet.getObject(metaData.getColumnName(6)));
        }

        System.out.println(resultSet);

        JdbcUtil.close(connection, statement, resultSet);
    }
}
1.5 通用查询方法声明分析
方法参数:
	String sql, Class<T> cls, Object... parameters;
	sql 目前需要进行查询操作的SQL语句
	cls 带有泛型,用于约束当前查询的目标数据类型是哪一个,同时提供Class对象,可以利用反射完成对象创建,赋值成
		员变量操作。
	parameters 对应当前SQL语句的参数

方法名:
	query

返回值类型:
	1. 泛型一定有
	2. 返回结果是一个T对应类型对象还是一组对象???
		List<T>

方法声明:
	public <T> List<T> query(String sql, Class<T> cls, Object... parameters);
/**
 * 通用query方法,用于处理select语句,用户可以提供当前查询对应的具体哪一个数据类型对象。需要告知
 * SQL语句和对应参数
 *
 * @param sql        目前需要进行查询操作的SQL语句
 * @param cls        带有泛型,用于约束当前查询的目标数据类型是哪一个,同时提供Class对象,可以利用反射完成对象创建,赋值成
 *                   员变量操作
 * @param parameters 对应当前SQL语句的参数
 * @param <T>        自定义泛型占位符
 * @return List集合,带有自定义泛型,泛型对应具体数据类型通过Class 参数确定,没有查询到任何数据返回null
 */
public <T> List<T> query(String sql, Class<T> cls, Object... parameters) {
    
    if (null == sql || null == cls) {
        throw new IllegalArgumentException("SQL or cls is null");
    }
    
    // 1. 准备必要的变量
    ResultSet resultSet = null;
    PreparedStatement statement = null;
    List<T> list = new ArrayList<>();
    
    // 2. 获取数据库连接对象
    Connection connection = JdbcUtil.getConnection();
    
    try {
        // 3. 预处理SQL语句,得到PreparedStatement对象
        statement = connection.prepareStatement(sql);
        
        // 4. 赋值SQL语句参数
        int parameterCount = statement.getParameterMetaData().getParameterCount();
        
        if (!(0 == parameterCount || null == parameters || parameterCount != parameters.length)) {
            for (int i = 0; i < parameters.length; i++) {
                statement.setObject(i + 1, parameters[i]);
            }
        }
        
        // 5. 执行SQL语句。得到结果集对象
        resultSet = statement.executeQuery();
        
        // 6. 通过while循环逐行解析ResultSet结果集
        // 结果集元数据对象
        ResultSetMetaData metaData = resultSet.getMetaData();
        
        // 通过结果集元数据对象,得到当前字段列数
        int columnCount = metaData.getColumnCount();
        
        while (resultSet.next()) {
            // 进入解析过程,证明存在一个对应数据对象
            T t = cls.getConstructor().newInstance();
            
            // for i 循环执行完毕,完整解析数据行内容赋值到当前 T 对象中
            for (int i = 1; i <= columnCount; i++) {
                // 根据列数获取对应的字段,同时字段名也是成员变量名
                String fieldName = metaData.getColumnName(i);
                // 获取对应字段名称的数据
                Object value = resultSet.getObject(fieldName);
                
                // 使用BeanUtils工具,给予指定符合JavaBean规范类对象,指定成员变量名字赋值对应数据。
                BeanUtils.setProperty(t, fieldName, value);
            }
            
            // 解析结果存入List集合中
            list.add(t);
        }
    } catch (SQLException | NoSuchMethodException | InstantiationException | IllegalAccessException 
            | InvocationTargetException e) {
        e.printStackTrace();
    } finally {
        JdbcUtil.close(connection, statement, resultSet);
    }
    
    // 如果当前List集合中没有元素,返回null,如果有元素返回当前List集合
    return list.size() == 0 ? null : list;
}
2. 数据库连接池
2.1 现有操作数据库的问题
	数据库操作使用Connection对象,在每一次执行SQL语句之后,都进行了关闭操作。在目前情况下,如果不关闭,会导致资源浪费,内存冗余。关闭资源习惯需要养成。
	打开关闭数据库操作过多针对资源也是浪费使用。
	类似于线程池操作的情况,满足用户使用数据库资源,同时降低数据库在创建和关闭时资源浪费。
2.2 需要考虑的参数和代理思想引入
数据库连接所需必要资源:
	1. driverClass
	2. jdbcUrl
	3. username
	4. password

关于数据库的性能配置
	1. 初始化数据库连接池对象Connection个数
	2. 阈值最多数据库连接对象Connection个数和最小个数
	3. 等待时间
	4. 每一个Connection对象最大的Statement个数
	
Connection对象需要结构存储
	底层参数Connection对象,采用的形式是链表结构,增删快

代理
	用户依然使用getConnection获取数据库连接对象,同时使用close关闭资源。操作流程没有变化。
	close方法在数据库连接池中不是真正的关闭当前Connection对象,而是将Connection对象归还到底层结构。
2.3 Druid数据库连接池使用
# druid.properties文件
# 文件名 druid.properties 存储在src目录下
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/student_info?useSSL=false
username=root
password=123456

# 初始化数据库连接池容量
initialSize=10

# 最大容量
maxActive=30

# TimeOut 等待超时时间
maxWait=2000
package util;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.FileInputStream;
import java.io.IOException;
import java.sql.*;
import java.util.Properties;

/**
 * JDBC数据库工具类
 * 目前数据库连接资源部分,已经替换为Druid数据库连接池。
 *      1. 数据库对象获取功能
 *      2. 数据库资源关闭功能
 */
public class JdbcUtil {
    /**
     * java.sql.DataSource 数据库连接池对象。
     */
    private static DataSource ds;

    static {
        try {
            // 加载src 目录下 druid.properties文件当前Properties对象中
            Properties properties = new Properties();
            properties.load(new FileInputStream("./src/druid.properties"));

            // 使用DruidDataSourceFactory Druid数据库连接池工厂类,调用创建数据库连接池方法,传入参数是Properties类对象
            ds = DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * JDBC工具类获取数据连接对象方法。如果用户提供的数据库连接信息有误,返回null
     *
     * @return java.sql.Connection对象,如果信息有误,返回null
     */
    public static Connection getConnection() {
        Connection connection = null;

        try {
            // 从数据库连接池中获取当前闲置数据库连接对象
            connection =  ds.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }

        return connection;
    }

    /**
     * 对外提供关闭数据库连接对象方法
     *
     * @param conn java.sql.Connection数据库连接对象
     */
    public static void close(Connection conn) {
        close(conn, null, null);
    }

    /**
     * 对外提供关闭数据库和数据库表搬运工对象方法
     *
     * @param conn java.sql.Connection 数据库连接对象
     * @param st   java.sql.Statement 数据库搬运工对象
     */
    public static void close(Connection conn, Statement st) {
        close(conn, st, null);
    }

    /**
     * 对外提供关闭数据库,数据库表搬运工对象以及数据库查询结果集对象方法
     *
     * @param conn java.sql.Connection 数据库连接对象
     * @param st   java.sql.Statement 数据库搬运工对象
     * @param rs   java.sql.ResultSet 数据库查询结果集对象
     */
    public static void close(Connection conn, Statement st, ResultSet rs) {
        try {
            close(rs, st, conn);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 类内私有化静态成员方法,用于提供关闭资源的一个能力
     *
     * @param res AutoCloseable 接口不定长参数,要求传入的是当前数据库操作对应的数据库资源
     * @throws Exception AutoCloseable 接口中close方法抛出异常
     */
    private static void close(AutoCloseable... res) throws Exception {
        for (AutoCloseable re : res) {
            if (re != null) {
                re.close();
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值