学习笔记整理_JDBC

JDBC

简介

Java Database Connectivity  通过java来访问数据库,本质上由一组访问数据库的接口和类组成
	优点:
	1. 同一组代码只需要稍微改动即可访问另外一种数据库,可扩展性较好
	2. 由一组接口组成,我们只针对接口编程,实现类由各数据库厂商实现
		打断点查看其实现类
	所有的关系型数据库必须遵循的规范。-- 解耦
	
使用到的jar包
	java.sql JDBC核心包
	javax.sql 扩展包,使用高级特性(如连接池)
	数据库的驱动包 mysql-connector-java

快速入门

/* 访问数据库步骤:
1. 通过DriverManager类注册驱动,系统自动完成 
2. 获取连接对象Connection,获取到表示连接成功
3. 通过Statement对象发送SQL语句到服务器端
4. 查询到的结果通过ResultSet对象封装*/

public static void main(String[] args) throws ClassNotFoundException, SQLException {
//注册驱动 加载到内存 字节码结构信息 可以自动加载 
        Class.forName("com.mysql.jdbc.Driver");// 使用到该类,执行了其静态代码块(代码块中进行了注册)mysql5 之后可以省略注册驱动的步骤。在jar包中,存在一个java.sql.Driver配置文件,文件中指定了com.mysql.jdbc.Driver
    // jdbc:mysql: 数据交互 解析规则
//创建连接
        Connection connection = DriverManager.getConnection("jdbc:mysql://192.168.182.133:3306/db4", "root", "root");
//获取语句对象
        Statement statement = connection.createStatement();
//要执行的语句对象
        String sql = "select * from USER";
//获取执行结果
        ResultSet resultSet1 = statement.executeQuery(sql);
//处理结果
        while (resultSet1.next()) {
            System.out.println(resultSet1.getInt("id") + resultSet1.getString("name"));
        }
//释放资源
        resultSet1.close();
        statement.close();
        connection.close();
    }

核心API

DriverManager类
	1. 注册数据库驱动
		确定JDBC实现类 --> Driver
	2. 获取数据库连接对象
Connection接口
	1. 获取执行者对象
	2. 管理事务
Statement接口
	封装sql语句,执行sql操作
ResultSet接口
	表示服务器查询结果集

DriverManager 类

JDBC连接数据库的四个参数

用户名: root
密码: root
驱动: com.mysql.jdbc.Driver
连接的URL
 	jdbc:mysql://localhost:3306/数据库名?characterEnconding=utf8
 	jdbc:mysql:///数据库名?参数名=参数值

获取连接方法

	static Connection getConnection(String url) 试图建立到给定数据库 URL 的连接。 
	static Connection getConnection(String url, Properties info) 试图建立到给定数据库 URL 的连接。 
	static Connection getConnection(String url, String user, String password) 试图建立到给定数据库 URL 的连接。 
Connection接口
获取执行者对象:
	Statement connection.createStatement()
	PreparedStatement connection.prepareStatement(sql);

管理事务:
	事务的管理需要在业务层实现,因为dao层的功能要给很多模块提供功能的支撑,而有些模块是不需要事务的
	void setAutoCommit(boolean autoCommit) 
	void commit()
	void rollback()
	
释放资源:
	void close()
Statement接口
封装SQL,执行SQL
	boolean excute(String sql)
	int excuteUpdate(String sql)
	ResultSet excuteQuery(String sql)
	SQL注入:
		利用特殊字符(系统漏洞)对系统进行攻击
		
public interface PreparedStatement extends Statement
	预编译对象,执行效率更高
	解决SQL注入问题,更安全
	代码可读性更高,参数用占位符'?'代替 

	setString(parameterIndex, str) 		parameterIndex第几个占位符
ResultSet接口
 boolean next() 游标(索引)移动到下一行,如果有数据,返回true
 获取结果集中数据:
     getInt("id")	列名	或 getInt(1)  列号  
     getString("name")

Clob/Blob 写入到数据库

Oracle中为Clob/Blob

MySQL中为TEXT/BLOB

Character Large Object large字符对象 在MySQL中对应的是TEXT数据类型
	TINYTEXT 256 字节
	TEXT 64KB
	MEDIUMTEXT 16MB
	LONGTEXT 4GB
	//写入:
	preparedStatement.setClob(index, inputStreamReader) 占位符,字符输入流
	//读取:
		resultSet.getString(conlumn)
		resultSet.getClob(column).getCharacterStream()

Binary Large Object 图片、视频、音频等大文件
	TINYBLOB 256字节
	BLOB 64K
	MEDIUMBLOB 16M
	LONGBLOB 4G	
	
	//写入:
	preparedStatement.setBlob(index, inputStream) 占位符,字节输入流
	//读取:
		resultSet.getBinaryStream(column)
		resultSet.getBlob(column).getBinaryStream()

数据库连接的URL

JDBC的URL=协议名+子协议名+数据源名。

a. 协议名总是“jdbc”。
b. 子协议名由JDBC驱动程序的编写者决定。
c. 数据源名也可能包含用户与口令等信息;这些信息也可单独提供。

几种常见的数据库连接

-------------------------------oracle------------------
驱动:oracle.jdbc.driver.OracleDriver
URL:jdbc:oracle:thin:@machine_name:port:dbname
注:machine_name:数据库所在的机器的名称;
      port:端口号,默认是1521

-------------------------------mysql-------------------
驱动:com.mysql.jdbc.Driver
URL:jdbc:mysql://machine_name:port/dbname
注:machine_name:数据库所在的机器的名称;
      port:端口号,默认3306    

---------------------------SQL Server------------------
驱动:com.microsoft.jdbc.sqlserver.SQLServerDriver
URL:jdbc:microsoft:sqlserver://<machine_name><:port>;DatabaseName=<dbname>
注:machine_name:数据库所在的机器的名称;
      port:端口号,默认是1433

--------------------------DB2--------------------------
驱动:com.ibm.db2.jdbc.app.DB2Driver
URL:jdbc:db2://<machine_name><:port>/dbname
注:machine_name:数据库所在的机器的名称;
      port:端口号,默认是5000

DataBaseMetaData

DataBaseMetaData :数据库的元信息
    java.sql.DataBaseMetaData 封装了整个数据库的综合信息。
例如:
	String getDatabaseProductName():获取数据库产品的名称
	int	getDatabaseProductVersion():获取数据库产品的版本号

ParameterMetaData

java.sql.ParameterMetaData 

通过 PreparedStatement 获取:
	ParameterMetaData getParameterMetaData()
	
ParameterMetaData 参数元信息 接口中方法:
 	int getParameterCount()  sql语句参数的个数
    String getParameterTypeName(int param) //MySQL对此方法不太支持
       获取指定列的特定于数据库的类型名称。 

ResultSetMetaData

java.sql.ResultSetMetaData

通过 ResultSet 接口获取:
	ResultSetMetaData getMetaData()

ResultSetMetaData 结果集元信息 接口中的方法:
	int getColumnCount() 获取列的总数
	String getColumnName(int columnIndex) 获取列名
	String getColumnTypeName(int columnIndex) 获取指定的列的类型

DataSource

简介:

​ 数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个。这项技术能明显提高对数据库操作的性能。

创建时
	连接对象不再由自己创建,而是系统启动的时候已经创建一定数量的连接,并且放在连接池中
使用时
	直接从连接池中去获取一个已经创建好的连接对象即可
关闭时
	不是真的关闭连接对象,而是将连接对象再放回到连接池中,供下一个用户使用

提高了创建连接对象的速度、连接对象的使用率

数据库连接池API

javax.sql.DataSource接口

Connection getConnection()
    从连接池中获取连接对象

    实现类由连接池的厂商实现
    必须实现此接口才能编写自己的连接池

c3p0 – 有自动回收空闲连接功能

目前使用它的开源项目有Hibernate,Spring等。

配置文件:	
	1. 文件名必须是:c3p0-config.xml
	2. 必须放在src目录(类路径下)
	3. 分为默认配置和命名配置
		可以指定多个数据库名 day24/day25
		可以指定不同厂商的数据库 Oracle/MySQL
		可以使用不同的连接池参数

<c3p0-config>
  <!-- 注释 -->
  <default-config>
    <property name="driverClass">com.mysql.jdbc.Driver</property>
    <property name="jdbcUrl">jdbc:mysql://localhost:3306/day25</property>
    <property name="user">root</property>
    <property name="password">root</property>
    
    <property name="initialPoolSize">5</property>
    <property name="maxPoolSize">10</property>
        <!-- 用户获取连接的最大等待时间,超时没有获取连接(池内连接都被占用)抛出异常-->
    <property name="checkoutTimeout">3000</property>
      <!-- maxIdleTime 最长闲置回收时间 默认是不回收-->
  </default-config>
	<!-- 命名配置 -->
  <named-config name="otherc3p0"> 
      ...
  </named-config>
</c3p0-config>

API
创建数据源(连接池)
	ComboPooledDataSource() 使用默认配置创建数据源
	ComboPooledDataSource(命名配置) 指定命名配置创建数据源
使用步骤:
	1. 导包 c3p0-0.9.5.2.jar && mchange-commons-java-0.2.12.jar
	2. 配置c3p0-config.xml,必须是这个名字
	3. 创建连接池
		ComboPooledDataSource ds = new ComboPooledDataSource("linux");
	4. 获取连接对象
		ds.getConnection()

Druid 阿里

号称为监控而生的数据库连接池,使用场景 ”双十一“ 、春运抢火车票。

配置文件:properties文件
	url=jdbc:mysql://localhost:3306/db1
	username=root
	password=root
	driverClassName=com.mysql.jdbc.Driver		(根据url自动识别,可省略)
	initialSize=5
	maxActive=10
	maxWait=2000

使用步骤:
	1. 得到配置文件的输入流
		Class类
			InputStream getResourceAsStream(String path)
	    		从src目录(类路径)下加载配置文件转为输入流对象
	2. 读取集合中数据,加载到集合
		Properties
			void load(InputStream inStream) 
	        	从输入流中读取属性列表(键和元素对)。 
	3. 通过属性集创建连接池
		DruidDataSourceFactory
			public static DataSource createDataSource(Properties properties)

Apache DBCP

Database Connection Pool

是Apache上的一个Java连接池项目,也是Tomcat使用的连接池组件。

自定义数据库连接池

思路:
	1. 如何存储多个数据库链接
 		集合
	2. 需不需要实现JDBC的规范
  		DataSource接口
	3. 什么时候获取数据库链接存入集合
  		构造方法
  		静态代码块
	4. 获取链接方法的实现
 		list.get(0)
 		从集合中移除对应的连接
	5. 关闭链接问题 (继承无法解决此问题)
  		装饰者模式
  		适配器模式
  		动态代理模式

自定义框架

框架解决的问题:

1. 解决了技术通用的问题
	在JavaEE体系中,有着各种各样的技术。不同的软件企业,根据自身的业务需求选择不同的技术,容易造成应用依赖技术,增加了项目开发实现的复杂性和技术风险性。企业项目中应该将应用的设计与实现技术解耦。

2. 提升了开发效率
	企业项目中使用框架,只需要专注实现业务需求。

3. 提升了系统稳定性
	一个成熟的框架,经过了在众多企业项目中的验证使用,稳定性有保障。

实现步骤
1. 使用第三方连接池
2. 获取连接
3. 实现通用的增删改方法
	利用元数据+属性描述器
	或元数据+反射
4. 实现通用的查询功能方法
	策略模式

调用方
public <T> T queryForObject(String sql, BeanResultSet<T> bean, Object... objects) {
    T obj = null;
    try {
        connection = dataSource.getConnection();
        preparedStatement = connection.prepareStatement(sql);
        ParameterMetaData parameterMetaData = preparedStatement.getParameterMetaData();
        int count = parameterMetaData.getParameterCount();
        if (count != objects.length) {
            throw new RuntimeException("参数个数不匹配");
        }
        for (int i = 0; i < count; i++) {
            preparedStatement.setObject(i + 1, objects[i]);
        }
        ResultSet resultSet = preparedStatement.executeQuery();
        obj = bean.handler(resultSet);
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        DataSourceUtil.close(connection, preparedStatement, resultSet);
    }
    return obj;
}

策略
@Override
    public T handler(ResultSet resultSet) {
        T bean = null;
        try {
            bean = beanClass.newInstance();//此处导致返回的结果为new Student()
            if (resultSet.next()) {
                ResultSetMetaData metaData = resultSet.getMetaData();
                int count = metaData.getColumnCount();
                for (int i = 1; i <= count; i++) {
                    String columnName = metaData.getColumnName(i);
                    Object value = resultSet.getObject(columnName);
                    //获取属性的getter和setter方法
                    PropertyDescriptor pd = new PropertyDescriptor(columnName.toLowerCase(), beanClass);
                    Method writeMethod = pd.getWriteMethod();
                    writeMethod.invoke(bean, value);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bean;
    }

属性描述器
PropertyDescriptor pd = new PropertyDescriptor(columnName, beanClass);//类的参数名,类
Method writeMethod = pd.getWriteMethod();//获取set方法
writeMethod.invoke(t, value);//实例对象,参数值

通用的查询方法
public <T> List<T> query(String sql, Class<T> clazz, Object... obj) {
        List<T> list = new ArrayList<>();
        try {
            //创建实例
            connection = dataSource.getConnection();
            preparedStatement = connection.prepareStatement(sql);
            ParameterMetaData parameterMetaData = preparedStatement.getParameterMetaData();
            int count = parameterMetaData.getParameterCount();
            if (count != obj.length) {
                throw new RuntimeException("参数不匹配");
            }
            for (int i = 0; i < count; i++) {
                preparedStatement.setObject(i + 1, obj[i]);
            }
            resultSet = preparedStatement.executeQuery();

            while (resultSet.next()) {
                T t = clazz.getConstructor().newInstance();
                Field[] fields = clazz.getDeclaredFields();
                for (Field field : fields) {
                    field.setAccessible(true);
                    field.set(t, resultSet.getObject(field.getName().toLowerCase()));
                }
                /*ResultSetMetaData metaData = resultSet.getMetaData();
                int columnCount = metaData.getColumnCount();
                for (int i = 0; i < columnCount; i++) {
                    PropertyDescriptor pd = new PropertyDescriptor(metaData.getColumnName(i + 1), clazz);
                    Method writeMethod = pd.getWriteMethod();
                    writeMethod.invoke(t, resultSet.getObject(i + 1));
                }*/
                list.add(t);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            DataSourceUtil.close(connection, preparedStatement, resultSet);
        }
        return list;
    }

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值