JDBC和连接池

发布于 2020-10-11  480 次阅读


      一,对JDBC的认识 

       Jdbc是一个持久层技术,它特定独立于数据库管理系统,通用的sql数据库存储操作的公共api,是访问数据库的一种规范和标准的接口,定义了java访问数据库的标准类库(如java.sql,javax.sql),jdbc可以方便访问数据库并操作数据库,jdbc也在其他语言中有同样的作用

jdbc带来的好处:

1,jdbc为访问不同sql数据库提供了统一的途径(通过jdbc可以访问MySQL和oracle等数据库)

2,jdbc屏蔽了底层细节,简化了开发过程

假如没有jdbc我们访问数据库:

我们要根据不同数据库类型,写一个驱动程序,过程复杂,门槛高

jdbc的两类接口:

1,程序操作的API接口:这是面向开发者的接口(开发者根据这是接口实现对数据库的操作)

2,数据库的驱动接口Driver:这是面向数据库厂商的接口(厂商根据jdbc提供的标准开发驱动Driver,实现不同数据库的复用性)

 

jdbc是持久层框架的基础:

mybatis就是基于jdbc的对象关系型映射框架,还有Hibernate框架……

二,jdbc的操作

jdbc的操作主要是连接+增删改查

一,数据库连接方式

jdbc的数据库的连接方式其实是固定的:注册驱动   ->  通过配置信息建立连接

1,直接创建Driver对象

Driver API

String div="jdbc:mysql://localhost:3306/wql";
Driver driver=new com.mysql.jdbc.Driver(); //直接new Driver对象,耦合度高直接依赖于MySQLjar包
Properties info =new Properties();
info.put("user", "root");
info.put("password", "123");
Connection connection=driver.connect(div, info);
System.out.print(connection);
connection.close();

2,反射创建Driver对象

通过class反射创建对象

class API

Driver driver=(Driver) Class.forName("com.mysql.jdbc.Driver").newInstance();//其实没必要这样,forname返回的是静态类,直接调用就行

String con="jdbc:mysql://localhost:3306/WQL";

Properties properties=new Properties();

properties.setProperty("user", "root");
properties.setProperty("password", "123");

Connection connection=driver.connect(con,properties);
connection.close();
System.out.print(connection);

3,DriverManager管理类创建连接(常用)

Manager是驱动的管理类可以创建连接,也可以操作其他信息

Driver API

String dirver="com.mysql.jdbc.Driver";

String connection="jdbc:mysql://localhost:3306/WQL";

Class.forName(dirver); 

Connection conn=DriverManager.getConnection(connection, "root", "123");//也可以转一个properties对象

conn.close();
System.out.print(conn);

4,外部配置文件建立连接

把配置参数写入文件中,在文件中配置修改信息,降低耦合,增加重用性

1,建立一个wql.propertise文件

user=root
password=123
Driver=com.mysql.jdbc.Driver
connection=jdbc:mysql://localhost:3306/WQL

2,将文件信息读取建立连接

//定义变量
String user=null;
String password=null;
String driver=null;
String connection=null;

//读取配置文件
FileInputStream input=new FileInputStream("wql.properties");

//读取文件流
Properties properties=new Properties();

properties.load(input);

user = properties.getProperty("user");
password = properties.getProperty("password");
driver = properties.getProperty("Driver");
connection=properties.getProperty("connection");

//连接
Driver d = (Driver) Class.forName(driver).newInstance();

Properties properties1=new Properties();
properties1.setProperty("user", user);
properties1.setProperty("password", password);

Connection conn=d.connect(connection,properties1);

//测试连接是否有效
System.out.print(conn);
conn.close();

二,Statement操作数据库

statement是jdbc的操作数对象之一,通过它可以实现对数据的DML操作

一,statement的获取

通过connection连接获取statement对象

Statement statement=conn.createStatement();

二,数据的插入和修改

插入和修改操作都依赖execte方法,在statement操作只能通过sql语句的字符串拼接来实

Statement statement=conn.createStatement();
String sql="insert into fq(id,name,random) values("+id+","+'"'+name+'"'+","+random+")";
int a=statement.executeUpdate(sql);
if(a==1) {
System.out.print("插入数据成功!");
}
close(statement);

sql语句的拼接对资源是一种极大的消耗,批量插入或者修改时,字符串都必须重新组装,浪费时间和内存

三,数据的查询

查询依赖executeQuery方法,它返回一个Resultset对象,通过遍历Resultset对象可以查询结果

Statement statemend=conn.createStatement();

String sql="select * from "+tablename;

ResultSet resultset=statemend.executeQuery(sql);

ArrayList<String[]> list=new ArrayList<>();
while(resultset.next()) {

Integer id= resultset.getInt(1);
String name= resultset.getString(2);
Integer random= resultset.getInt(3);
String[] wql=new String[3];
wql[0]=id.toString();
wql[1]=name;
wql[2]=random.toString();
list.add(wql);
}
Iterator l= list.iterator();
while(l.hasNext()) {
String[] a=(String[]) l.next();
for(String aq:a) {

System.out.print(aq+"\t");

}
System.out.println();
}

close(statemend);
resultset.close();

三,PreparedStatement操作数据库

preparedstatement严格上它是statement的子类,它是在statement上的弊端的补全和结构的优化

preparedstatement最大的改良就是把statement的sql字符串拼接改成了参数注入,实现了预编译sql语句,sql语句被预编译进入prepaoedstatement对象中,使用这个对象可以高效的重复执行操作

一,创建preparedstatement对象

connection创建preparedstatement的API

String sql="insert into "+tablename+" values(?,?,?)";
PreparedStatement s=(PreparedStatement) conn.prepareStatement(sql);

二,数据的插入和修改

String sql="insert into "+tablename+" values(?,?,?)";
PreparedStatement s=(PreparedStatement) conn.prepareStatement(sql);
// System.out.print(sql);
s.setInt(1, 7);
s.setString(2, "las");
s.setInt(3, 100);
s.executeUpdate();//执行语句
close(s);
s.close();

三,数据查询

 String sql="select * from ?";
PreparedStatement s= conn.prepareStatement(sql);
s.setString(1, "test");
ResultSet ss= s.executeQuery(sql);
while(ss.next()) {
System.out.print(ss.getString(2)+"===="+ss.getInt("id"));
}

四,statement和preparedstatement的比较

两者区别主要是从安全性和效率两个方面比较:

                                                          1,安全性:前者sql的执行主要是通过字符串拼接实现的,容易产生sql注入,而后者是预编译在方法内部运行,不会产生注入问题

                                                          2,效率:字符串的拼接本身也是极其浪费资源的,批量操作每一次执行都需要拼接一次字符串浪费了资源,后者预编译sql,可以高效重复执行

一,SQL注入问题

通过字符串的拼接原理,人为产生一个新sql

Statement statement=conn.createStatement();
//注入语句
String sql1="where id>100";
//正常语句
String sql="select * from test";
//注入sql
statement.executeQuery(sql+sql1);

二,批量操作

批量操作的几种方式:

1,statement实现

2,preparedstatement实现

3,addbatch缓冲池实现

//批量插入数据
public static void main(String[] args) throws Exception {

jdbctool c=new jdbctool();
Connection conn = c.getconn();
long a= System.currentTimeMillis();

test3(conn);
long a1=System.currentTimeMillis();
System.out.print((a1-a)/1000);
}

//方式1:statement,字符串拼接
public static void test1(Connection conn) throws SQLException {

Statement statement=conn.createStatement();
for(int a=2;a<=100000;a++) {
String sql="insert into test values ("+a+","+a+")";
statement.execute(sql);

}
statement.close();
conn.close();
//花费时间:73秒
}
//方式2:preparedstatement
public static void test2(Connection conn) throws SQLException {
String sql="insert into test values (?,?)";
PreparedStatement prepared =conn.prepareStatement(sql);
for(int i=2;i<=100000;i++) {
prepared.setInt(1, i);
prepared.setString(2,""+i);
prepared.execute();
}
prepared.close();
conn.close();
//花费时间:53秒
}

//方式3:preparedstatement的addbatch缓冲池(相当于io的buffer缓存区)
//注:在sql中默认一条语句就是一个事务直接提交,用缓冲区我们不要自动提交,设置为手动提交(?rewriteBatchedStatements=true加在url配置后面)
public static void test3(Connection conn) throws SQLException {
String sql="insert into test values (?,?)";
PreparedStatement prepared =conn.prepareStatement(sql); 
for(int i=2;i<=100000;i++) {//多了个0
prepared.setInt(1, i);
prepared.setString(2, ""+i);
//加入缓冲区
prepared.addBatch();
if(i%50000==0) {
prepared.executeBatch();
prepared.clearBatch();
}
}
prepared.close();
conn.close();
//花费时间:34秒
}

三,Blob类型数据的插入

Blob类型数据算是MySQL中一种有意思的数据类型

blob类型介绍:

1,blob类型是MySQL中一种二进制大型对象,是一个存储大型数据的容器,它能容纳不同大小的数据(像图片和音频)

2,插入blob对象必须要使用PreparedStatement对象,因为它不能通过字符串拼接得到(它的输入有两种:输入io流,blob对象)

3,MySQL中有四种blob类型(除最大容量不同,其他一致)

   类型              大小(单位:字节)
   TinyBlob            最大255
   Blob                最大65k
   MediumBlob          最大16M
   LongBlob            最大4G

注:如果过大数据库性能会下降,超过容量会报错

PreparedStatement插入blob API:

Connection conn=null;
jdbctool tool=new jdbctool();
conn=tool.getconn();
PreparedStatement prepared = conn.prepareStatement("insert into blobs values (?,?)");
InputStream wql = new FileInputStream(new File("4-1.jpg"));
prepared.setInt(1, 1);
prepared.setBlob(2, wql);
prepared.execute();
prepared.close();
conn.close();

三,数据库连接池

数据库连接创建和关闭读极其耗费资源,而数据库连接池就很好的弥补了这个缺陷,连接池提高了连接的重用性

连接池的作用:

1,统一管理连接创建和销毁,减少资源的占用

2,连接的可重用性提高,提高的程序效率

               

连接池三种类型jar包下载:

连接池驱动

DataSource接口(重点):

这是java提供,数据库连接池的必须实现的一个接口,主要由数据库厂商实现

而DataSource又继承ConnomDatasource方法

常用的连接池框架(主要介绍三种):

1,C3P0:是一个开源组织提供的数据库连接池,速度较慢,但稳定性较好

2,DBCH:Apache提供是Tomcat自带的数据库连接池,相比较C3P0快,当自身有bug

3,Druid:是阿里的连接池,集成了各数据库连接池的优点(是现在主流)

一,C3P0

第一种实现(手写配置):

 //获取c3p0连接池,
ComboPooledDataSource pool= new ComboPooledDataSource();
//基本参数设置
pool.setDriverClass("com.mysql.jdbc.Driver");
pool.setJdbcUrl("jdbc:mysql://localhost/wql");
pool.setUser("root");
pool.setPassword("123");
//获取连接
Connection conn=pool.getConnection();
System.out.print(conn);

第二种实现(配置文件配置):

这里配置文件可以是prepaoties文件也可以是xml文件,以xml文件为例

新建一个xml文件:这个文件命名必须是c3p0默认配置文件名,c3p0-config.xml,c3p0有自己独特的xml风格

c3p0-config.xml:文件格式和配置信息详情,只是一部分

<?xml version="1.0" encoding="UTF-8"?>
<!--c3p0根标签  -->

<c3p0-config>

<!--default-config 默认配置 只读取一次 -->
  <default-config>
    
    <!-- 连接超时时间 -->
    <property name="checkoutTimeout">30000</property>
   
    <!-- 每个多少秒检查池中的空闲连接 -->
    <property name="idleConnectionTestPeriod">30</property>
    
    <!-- 初始化连接池大小 -->
    <property name="initialPoolSize">10</property>
    
    <!-- 最大空闲时间,指定时间连接未使用,连接被丢弃 -->
    <property name="maxIdleTime">30</property>
	
	<!-- 设置最小和最大连接池连接数 -->
    <property name="maxPoolSize">100</property>
    <property name="minPoolSize">10</property>
    <property name="maxStatements">200</property>
	
	<!-- 每一个user用户可以当指定连接数和缓存PreparedStatement的总数 -->
    <user-overrides user="test-user">
      <property name="maxPoolSize">10</property>
      <property name="minPoolSize">1</property>
      <property name="maxStatements">0</property>
    </user-overrides>

  </default-config>

  <!--name-config 自定义配置 需要声明name  如果c3p0DataSource调用xml自定义配置需要带name参数  -->
  <named-config name="intergalactoApp"> 
    <property name="acquireIncrement">50</property>
    <property name="initialPoolSize">100</property>
    <property name="minPoolSize">50</property>
    <property name="maxPoolSize">1000</property>

    <!-- intergalactoApp adopts a different approach to configuring statement caching -->
    <property name="maxStatements">0</property> 
    <property name="maxStatementsPerConnection">5</property>
  </named-config>
</c3p0-config>

自动文件配置:

c3p0-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--c3p0根标签 -->

<c3p0-config>
<named-config name="wql"> 
<!--连接元数据信息-->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcurl">jdbc:mysql//localhost:3306/WQL></property>
<property name="user">root</property>
<property name="password">123</property>
<!-- intergalactoApp adopts a different approach to configuring statement caching -->
<property name="maxStatements">0</property> 
<property name="maxStatementsPerConnection">5</property>
</named-config>
</c3p0-config>

C3P0java文件:

public static void main(String[] args) throws SQLException {
//指定数据名
ComboPooledDataSource pool =new ComboPooledDataSource("wql");
Connection conn=pool.getConnection();
System.out.print(conn);
}

 

 

 

 

 

 


路漫漫其修远兮,吾将上下而求索