JDBC概述:
JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。是Java访问数据库的标准规范
JDBC提供了一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序。
JDBC需要连接驱动,驱动是两个设备要进行通信,满足一定通信数据格式,数据格式由设备提供商规定,设备提供商为设备提供驱动软件,通过软件可以与该设备进行通信。
Java提供访问数据库规范称为JDBC,而生产厂商提供规范的实现类称为驱动。JDBC是接口,驱动是接口的实现,没有驱动将无法完成数据库连接,从而不能操作数据库!每个数据库厂商都需要提供自己的驱动,用来连接自己公司的数据库,也就是说驱动一般都由数据库生成厂商提供。
数据库连接池:
实际开发中“获得连接”或“释放资源”是非常消耗系统资源的两个过程,为了解决此类性能问题,通常情况我们采用连接池技术,来共享连接Connection。这样我们就不需要每次都创建连接、释放连接了,这些操作都交给了连接池。
原理:一开始先在内存中开辟一块空间,在池中存放多个连接对象,后面需要连接的话,直接从池中取,不需要自己创建,用完记得归还(调用Connection的close()方法也不会真的关闭Connection,而是把Connection“归还”给池),以确保连接对象能循环使用。
Java为数据库连接池提供了公共的接口:javax.sql.DataSource,各个厂商需要让自己的连接池实现这个接口。这样应用程序可以方便的切换不同厂商的连接池!
先来熟悉一下JDBC吧!
JDBC开发步骤
- 注册驱动.
导入jar包,此处以mysql为例:
JDBC规范定义驱动接口:java.sql.Driver,MySql驱动包提供了实现类:com.mysql.jdbc.Driver
DriverManager工具类,提供注册驱动的方法 registerDriver(),方法的参数是java.sql.Driver,所以我们可以通过如下语句进行注册:
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
以上代码不推荐使用,存在两方面不足
1).硬编码,后期不易于程序扩展和维护
2.)驱动被注册两次。
通常开发我们使用Class.forName() 加载一个使用字符串描述的驱动类。
如果使用Class.forName()将类加载到内存,该类的静态代码将自动执行。
通过查询com.mysql.jdbc.Driver源码,我们发现Driver类“主动”将自己进行注册
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
……
}
-
获得连接.
获取连接需要方法 DriverManager.getConnection(url,username,password),三个参数分别表示,url 需要连接数据库的位置(网址) user用户名 password 密码
url比较复杂,下面是mysql的url:
jdbc:mysql://localhost:3306/mydb
JDBC规定url的格式由三部分组成,每个部分中间使用冒号分隔。
第一部分是jdbc,这是固定的;
第二部分是数据库名称,那么连接mysql数据库,第二部分当然是mysql了;
第三部分是由数据库厂商规定的,我们需要了解每个数据库厂商的要求,mysql的第三部分分别由数据库服务器的IP地址(localhost)、端口号(3306),以及DATABASE名称(mydb)组成。 -
获得语句执行平台
String sql = “某SQL语句”;
获取Statement语句执行平台:Statement stmt = con.createStatement(); -
执行sql语句
常用方法:
int executeUpdate(String sql); --执行insert update delete语句.
ResultSet executeQuery(String sql); --执行select语句.
boolean execute(String sql); --执行select返回true 执行其他的语句返回false. -
处理结果
执行insert、update、delete,返回int,无需处理
ResultSet实际上就是一张二维的表格,我们可以调用其boolean next()方法指向某行记录,当第一次调用next()方法时,便指向第一行记录的位置,这时就可以使用ResultSet提供的getXXX(int col)方法(与索引从0开始不同个,列从1开始)来获取指定列的数据:
rs.next();//指向第一行
rs.getInt(1);//获取第一行第一列的数据
常用方法:
Object getObject(int index) / Object getObject(String name) 获得任意对象
String getString(int index) / Object getObject(String name) 获得字符串
int getInt(int index) / Object getObject(String name) 获得整形
double getDouble(int index) / Object getObject(String name) 获得双精度浮点型 -
释放资源
与IO流一样,使用后的东西都需要关闭!关闭的顺序是先得到的后关闭,后得到的先关闭。
rs.close();
stmt.close();
con.close();
上代码:
package com.util;
/*
* 实现JDBC的工具类
* 定义方法,直接返回数据库的连接对象
*
* 写关闭方法
*/
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class JDBCUtils {
private JDBCUtils(){}
private static Connection con ;
static{
try{
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/my";
String username="root";
String password="root";
con = DriverManager.getConnection(url, username, password);
}catch(Exception ex){
throw new RuntimeException(ex+"数据库连接失败");//抛这个异常时代码不往下执行
}
}
/*
* 定义静态方法,返回数据库的连接对象
*/
public static Connection getConnection(){
return con;
}
public static void close(Connection con,Statement stat){
if(stat!=null){
try{
stat.close();
}catch(SQLException ex){}
}
if(con!=null){
try{
con.close();
}catch(SQLException ex){}
}
}
public static void close(Connection con,Statement stat , ResultSet rs){
if(rs!=null){
try{
rs.close();
}catch(SQLException ex){}
}
if(stat!=null){
try{
stat.close();
}catch(SQLException ex){}
}
if(con!=null){
try{
con.close();
}catch(SQLException ex){}
}
}
}
实际开发中一般会把数据库的配置写在配置文件中,方便后期维护修改,
以database.properties为例,
driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/my
username=root
password=root
读database.properties文件:
package com.properties;
import java.io.FileInputStream;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.Properties;
/*
* 加载properties配置文件
* IO读取文件,键值对存储到集合
* 从集合中以键值对方式获取数据库的连接信息,完成数据库的连接
*/
public class PropertiesDemo {
public static void main(String[] args) throws Exception{
/*FileInputStream fis = new FileInputStream("src/database.properties");
System.out.println(fis);*/
//发布给用户的是bin文件,所以会报空指针异常
//所以
//使用类的加载器
InputStream in = PropertiesDemo.class.getClassLoader().getResourceAsStream("database.properties");
//ClassLoader getClassLoader() 返回类的类加载器。
//InputStream getResourceAsStream(String name) 返回用于读取指定资源的输入流。
System.out.println(in);
Properties pro = new Properties();
//导入输入流。
pro.load(in);
//获取集合中的键值对
String driverClass=pro.getProperty("driverClass");
String url = pro.getProperty("url");
String username = pro.getProperty("username");
String password = pro.getProperty("password");
Class.forName(driverClass);
Connection con = DriverManager.getConnection(url, username, password);
System.out.println(con);
}
}
写一个工具类就是:
package com.util;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.Properties;
/*
* 编写数据库连接的工具类,JDBC工具类
* 获取连接对象采用读取配置文件方式
* 读取文件获取连接,执行一次,static{}
*/
public class JDBCUtilsConfig {
private static Connection con ;
private static String driverClass;
private static String url;
private static String username;
private static String password;
static{
try{
readConfig();
Class.forName(driverClass);
con = DriverManager.getConnection(url, username, password);
}catch(Exception ex){
throw new RuntimeException("数据库连接失败");
}
}
private static void readConfig()throws Exception{
InputStream in = JDBCUtilsConfig.class.getClassLoader().getResourceAsStream("database.properties");
Properties pro = new Properties();
pro.load(in);
driverClass=pro.getProperty("driverClass");
url = pro.getProperty("url");
username = pro.getProperty("username");
password = pro.getProperty("password");
}
public static Connection getConnection(){
return con;
}
}
insertdemo:
package com.mysql;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
/*
JDBC操作数据库的步骤
1.注册驱动
告知JVM使用的是哪一个数据库的驱动
2.获得连接
使用JDBC中的类,完成对MySQL数据库的连接
3.获得语句执行平台
通过连接对象获取对SQL语句的执行者对象
4.执行sql语句
使用执行者对象,向数据库执行SQL语句
获取到数据库的执行后的结果
5.处理结果
6.释放资源 一堆close()
*/
public class InsertTest {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//1.注册驱动 反射技术,将驱动类加入到内存
Class.forName("com.mysql.jdbc.Driver");
//2.获得数据库连接 DriverManager类中静态方法
//static Connection getConnection(String url, String user, String password)
String url = "jdbc:mysql://localhost:3306/my";
String username="root";
String password="root";
Connection con = DriverManager.getConnection(url, username, password);
//3.获得语句执行平台, 通过数据库连接对象,获取到SQL语句的执行者对象
// con对象调用方法 Statement createStatement() 获取Statement对象,将SQL语句发送到数据库
Statement stat = con.createStatement();
// 4.执行sql语句
// 通过执行者对象调用方法执行SQL语句,获取结果
int row = stat.executeUpdate
("INSERT INTO jackson(username,password) VALUES('momo','123')");
System.out.println(row);
//6.释放资源 一堆close()
stat.close();
con.close();
}
}
selectdemo:
package com.mysql;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class SelectTest {
public static void main(String[] args) throws Exception {
//1. 注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2. 获取连接对象
String url = "jdbc:mysql://localhost:3306/my";
String username="root";
String password="root";
Connection con = DriverManager.getConnection(url, username, password);
//3 .获取执行SQL 语句对象
Statement stat = con.createStatement();
// 拼写查询的SQL
String sql = "SELECT * FROM jackson";
//4. 调用执行者对象方法,执行SQL语句获取结果集
// ResultSet executeQuery(String sql) 执行SQL语句中的select查询
// 返回值ResultSet接口的实现类对象,实现类在mysql驱动中
ResultSet rs = stat.executeQuery(sql);
//5 .处理结果集
// ResultSet接口方法 boolean next() 返回true,有结果集,返回false没有结果集
while(rs.next()){
//获取每列数据,使用是ResultSet接口的方法 getXX方法参数中,建议写String列名
System.out.println(rs.getString("username")+" "+rs.getString("password")+
" "+rs.getString("sex")+" "+rs.getString("age"));
}
rs.close();
stat.close();
con.close();
}
}
如果用Statement ,会存在注入攻击的情况,大概意思就是防止有的人登录时输入密码后,加上"or" 1=1,这样就能查出所有结果集啦,这时就有了子接口PreparedStatement预编译sql,参数用?(占位符)
上代码:
package com.mysql;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Scanner;
/*
* Java程序实现用户登录,用户名和密码,数据库检查
* 防止注入攻击
* Statement接口实现类,作用执行SQL语句,返回结果集
* 有一个子接口PreparedStatement (SQL预编译存储,多次高效的执行SQL)
* PreparedStatement的实现类数据库的驱动中,如何获取接口的实现类
*
* 是Connection数据库连接对象的方法
* PreparedStatement prepareStatement(String sql)
*/
public class PrepareStatementTest {
public static void main(String[] args) throws Exception {
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/my";
String username = "root";
String password = "root";
Connection con = DriverManager.getConnection(url, username, password);
Scanner sc = new Scanner(System.in);
String user = sc.nextLine();
String pass = sc.nextLine();
//执行SQL语句,数据表,查询用户名和密码,如果存在,登录成功,不存在登录失败
String sql = "SELECT * FROM jackson WHERE username=? AND PASSWORD=?";
//调用Connection接口的方法prepareStatement,获取PrepareStatement接口的实现类
//方法中参数,SQL语句中的参数全部采用问号占位符
PreparedStatement pst = con.prepareStatement(sql);
System.out.println(pst);
//调用pst对象set方法,设置问号占位符上的参数
pst.setObject(1, user);
pst.setObject(2, pass);
//调用方法,执行SQL,获取结果集
ResultSet rs = pst.executeQuery();
while(rs.next()){
System.out.println(rs.getString("username")+" "+rs.getString("password"));
}
rs.close();
pst.close();
con.close();
}
}
啰嗦了一大堆,赶紧步入正题啦,不要忘记本文名称数据库连接池
先搭建一个简单的数据库连接池熟悉一下概念:
连接池工具类要实现DataSource接口,
package com.connectionpool;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import javax.sql.DataSource;
import com.util.JDBCUtils;
public class MyDataSource implements DataSource{
//一开始先往池子里面放10个连接
List <Connection> list = new ArrayList<Connection>();
public MyDataSource() {
for (int i = 0; i < 10; i++) {
Connection conn = JDBCUtils.getConnection();
list.add(conn);
}
}
//该连接池对外公布的获取连接的方法
@Override
public Connection getConnection() throws SQLException {
// TODO Auto-generated method stub
//来拿连接的时候,先看看,池子里面还有没有,没有要扩容。
if(list.size() == 0 ){
for (int i = 0; i < 5; i++) {
Connection conn = JDBCUtils.getConnection();
list.add(conn);
}
}
Connection conn = list.remove(0);
return conn;
}
//用完之后,记得归还
public void addBack(Connection conn){
list.add(conn);
}
@Override
public PrintWriter getLogWriter() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public int getLoginTimeout() throws SQLException {
// TODO Auto-generated method stub
return 0;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
// TODO Auto-generated method stub
return null;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
// TODO Auto-generated method stub
return false;
}
@Override
public Connection getConnection(String username, String password)
throws SQLException {
// TODO Auto-generated method stub
return null;
}
}
常见的数据库连接池:
dbcp、c3p0
dbcp:
第一步:导包
除了之前的mysql-connector-java-5.1.37-bin.jar
另导入
第二步:创建DataSource接口的实现类对象
实现类, org.apache.commons.dbcp
BasicDataSource dataSource = new BasicDataSource();
第三步:设置数据库连接基本信息,通过对象方法setXXX设置
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/my");
dataSource.setUsername("root");
dataSource.setPassword("root");
第四步:获得连接
Connection con = dataSource.getConnection();
一个简单的dbcp连接池就出来了:
package com.dbcp;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.apache.commons.dbcp.BasicDataSource;
import com.util.JDBCUtils;
/*
* 连接池jar包中,定义好一个类 BasicDataSource
* 实现类数据源的规范接口 javax.sql.DataSource
*/
public class DataSoruceDemo {
public static void main(String[] args) {
Connection con = null;
PreparedStatement pst = null;
//1.创建DataSource接口的实现类对象
//实现类, org.apache.commons.dbcp
BasicDataSource dataSource = new BasicDataSource();
//2.连接数据库的4个最基本信息,通过对象方法setXXX设置进来
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/my");
dataSource.setUsername("root");
dataSource.setPassword("root");
try{
//3.调用对象方法getConnection获取数据库的连接
con = dataSource.getConnection();
System.out.println(con);
}catch(SQLException ex){
// System.out.println(ex);
throw new RuntimeException("数据库连接失败");
}finally{
//BasicDataSource做了处理,这里的关闭是把连接还回去
JDBCUtils.close(con, pst);
}
}
}
补充下通过读配置文件获取连接的,这里需要BasicDataSourceFactory类的createDataSource(Properties properties)方法:
dbcpconfig.properties:
#连接设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/my
username=root
password=root
#<!-- 初始化连接 -->
initialSize=10
#最大连接数量
maxActive=50
#<!-- 最大空闲连接 -->
maxIdle=20
#<!-- 最小空闲连接 -->
minIdle=5
#<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 -->
maxWait=60000
#JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:[属性名=property;]
#注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们。
connectionProperties=useUnicode=true;characterEncoding=gbk
#指定由连接池所创建的连接的自动提交(auto-commit)状态。
defaultAutoCommit=true
#driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。
#可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=READ_UNCOMMITTED
读配置文件:
package com.dbcp;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;
import org.junit.Test;
import com.util.JDBCUtils;
public class DbcpByReadConfig {
@Test
public void testDBCP(){
Connection conn = null;
PreparedStatement ps = null;
try {
BasicDataSourceFactory factory = new BasicDataSourceFactory();
Properties properties = new Properties();
InputStream is = getClass().getClassLoader().getResourceAsStream("dbcpconfig.properties");
properties.load(is);
DataSource dataSource = factory.createDataSource(properties);
conn = dataSource.getConnection();
String sql = "insert into jackson(username,password) values( ? , ?)";
ps = conn.prepareStatement(sql);
ps.setString(1, "yyqx");
ps.setString(2, "123456");
ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
}finally {
JDBCUtils.close(conn, ps);
}
}
}
c3po:
c3p0更常用
首先导包:
上代码,先来不读配置文件的:
package com.c3p0;
import java.sql.Connection;
import java.sql.PreparedStatement;
import org.junit.Test;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.util.JDBCUtil;
public class C3P0Demo {
@Test
public void testC3P0 (){
Connection conn = null;
PreparedStatement ps = null;
try {
//1. 创建datasource
ComboPooledDataSource dataSource = new ComboPooledDataSource();
//2. 设置连接数据的信息
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost/mydb");
dataSource.setUser("root");
dataSource.setPassword("root");
//2. 得到连接对象
conn = dataSource.getConnection();
String sql = "insert into user(uid,username,password) values(?,? , ?)";
ps = conn.prepareStatement(sql);
ps.setString(1, "001");
ps.setString(2, "admi234n");
ps.setString(3, "123");
ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
}finally {
JDBCUtil.release(conn, ps);
}
}
}
再来通过读配置文件的:
这里需要特别注意配置文件名字只能叫c3p0-config.xml
因为ComboPooledDataSource类内部去扫这个文件了
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<!-- default-config 默认的配置, -->
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost/mydb</property>
<property name="user">root</property>
<property name="password">root</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>
</default-config>
<!-- This app is massive! -->
<named-config name="oracle">
<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>
<!-- he's important, but there's only one of him -->
<user-overrides user="master-of-the-universe">
<property name="acquireIncrement">1</property>
<property name="initialPoolSize">1</property>
<property name="minPoolSize">1</property>
<property name="maxPoolSize">5</property>
<property name="maxStatementsPerConnection">50</property>
</user-overrides>
</named-config>
</c3p0-config>
默认的是进行mysql配置,也可指定oracle等数据库的配置
package com.c3p0;
import java.sql.Connection;
import java.sql.PreparedStatement;
import org.junit.Test;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.util.JDBCUtil;
public class C3P0ByConfig {
@Test
public void testC3P0(){
Connection conn = null;
PreparedStatement ps = null;
try {
//默认会找 xml 中的 default-config 分支。
ComboPooledDataSource dataSource = new ComboPooledDataSource();
//若需要oracle,则
//ComboPooledDataSource dataSourceora = new ComboPooledDataSource("oracle");
//2. 得到连接对象
conn = dataSource.getConnection();
String sql = "insert into user(uid,username,password) values(?,? , ?)";
ps = conn.prepareStatement(sql);
ps.setString(1, "002");
ps.setString(2, "my");
ps.setString(3, "123345");
ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
}finally {
JDBCUtil.release(conn, ps);
}
}
}
整合成一个C3P0Util:(不要忘记c3p0-config.xml),后期用c3p0连接池需要这两就行
package com.util;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import javax.sql.DataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class C3P0Util {
private static DataSource dataSource = new ComboPooledDataSource();
private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
// 直接可以获取一个连接池
public static DataSource getDataSource() {
return dataSource;
}
// 获取连接对象
public static Connection getConnection() throws SQLException {
Connection con = tl.get();
if (con == null) {
con = dataSource.getConnection();
tl.set(con);
}
return con;
}
// 开启事务
public static void startTransaction() throws SQLException {
Connection con = getConnection();
if (con != null) {
con.setAutoCommit(false);
}
}
// 事务回滚
public static void rollback() throws SQLException {
Connection con = getConnection();
if (con != null) {
con.rollback();
}
}
// 提交并且 关闭资源及从ThreadLocall中释放
public static void commitAndRelease() throws SQLException {
Connection con = getConnection();
if (con != null) {
con.commit(); // 事务提交
con.close();// 关闭资源
tl.remove();// 从线程绑定中移除
}
}
// 关闭资源方法
public static void closeConnection() throws SQLException {
Connection con = getConnection();
if (con != null) {
con.close();
}
}
public static void closeStatement(Statement st) throws SQLException {
if (st != null) {
st.close();
}
}
public static void closeResultSet(ResultSet rs) throws SQLException {
if (rs != null) {
rs.close();
}
}
}
下面补充Hikari连接池,有缘后期来详讲!
导包:
package com.util;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
public class HikariUtil{
private static HikariDataSource datasource;
public static synchronized Connection getCon() throws SQLException{
if(datasource == null){
HikariConfig config = new HikariConfig();
// config.setDriverClassName("oracle.jdbc.driver.OracleDriver");
// config.setJdbcUrl("jdbc:oracle:thin:@ip:port:orcl");
// config.setUsername("username");
// config.setPassword("password");
config.setDriverClassName("com.mysql.jdbc.Driver");
config.setJdbcUrl("jdbc:mysql://localhost/mydb");
config.setUsername("root");
config.setPassword("root");
config.setMaximumPoolSize(100);
config.addDataSourceProperty("cachePrepStmts", "true");
config.addDataSourceProperty("prepStmtCacheSize", "250");
config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
config.setConnectionTimeout(20*1000);
datasource = new HikariDataSource(config);
}
return datasource.getConnection();
}
public static void close(Connection con,PreparedStatement pst){
try {
if(con!=null){
con.close();
}
if(pst!=null){
pst.close();
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
遇到的坑:
大概意思就是mysql驱动版本低了!
解决:换版本
其实这里就是想记录下下载驱动的步骤emmmmm
mysql官网:https://www.mysql.com/
选择Downloads ,往下滑找到:
接下来找到下图打开:
Select Operating System:
点download后选择下图就可以了
又有坑:
解决:url换成
config.setJdbcUrl("jdbc:mysql://localhost/mydb?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC");
数据库连接池只是获取了数据库的连接操作,并不对从数据库中取到的数据进行处理,下面介绍DBUtils来对返回的数据进行处理
DBUtils核心类:
QueryRunner:提供对sql语句操作的API.
ResultSetHandler接口:用于定义select操作后,怎样封装结果集.
增删改都用update(),查询用query()
QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
//增删改
public static void update(){
String sql = "INSERT INTO sort (sname,sprice,sdesc)VALUES(?,?,?)";
Object[] params = {"水果",100.12,"刚刚上市的核桃"};
try{
int row = qr.update(sql, params);
System.out.println(row);
}catch(SQLException ex){
throw new RuntimeException("数据添加失败");
}
}
//查询,ResultSetHandler为接口,直接new接口的匿名实现类
public static void select() throws SQLException{
Sort sort = qr.query("select * from sort where sid = ?", new ResultSetHandler<Sort>(){
@Override
public Sort handle(ResultSet rs) throws SQLException {
Sort sort = new Sort();
while(rs.next()){
String sname = rs.getString("sname");
Double sprice = rs.getDouble("sprice");
String sdesc = rs.getString("sdesc");
sort.setSname(sname);
sort.setSprice(sprice);
sort.setSdesc(sdesc);
}
return sort;
}
}, 6);
System.out.println(sort.toString());
}
以上是直接new接口的匿名实现类,也可new接口的实现类
常见的ResultSetHandler接口实现类:
实现类 | 描述 |
---|---|
BeanHandler | 将结果集中第一条记录封装到一个指定的javaBean中。 |
BeanListHandler | 将结果集中每一条记录封装到指定的javaBean中,将这些javaBean在封装到List集合中 |
ArrayHandler | 将结果集中的第一条记录封装到一个Object[]数组中,数组中的每一个元素就是这条记录中的每一个字段的值 |
ArrayListHandler | 将结果集中的每一条记录都封装到一个Object[]数组中,将这些数组在封装到List集合中。 |
MapHandler | 将结果集第一行封装到Map集合中,Key 列名, Value 该列数据 |
MapListHandler | 将结果集第一行封装到Map集合中,Key 列名, Value 该列数据,Map集合存储到List集合 |
ScalarHandler | 它是用于单数据。例如select count(*) from 表操作。 |
ColumnListHandler | 将结果集中指定的列的字段值,封装到一个List集合中 |
上代码:
package com.dbutils;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.ArrayHandler;
import org.apache.commons.dbutils.handlers.ArrayListHandler;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ColumnListHandler;
import org.apache.commons.dbutils.handlers.MapHandler;
import org.apache.commons.dbutils.handlers.MapListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import com.entity.Sort;
import com.util.C3P0Utils;
import com.util.JDBCUtilsConfig;
/*
* QueryRunner数据查询操作:
* 调用QueryRunner类方法query(Connection con,String sql,ResultSetHandler r, Object..params)
* ResultSetHandler r 结果集的处理方式,传递ResultSetHandler接口实现类
* Object..params SQL语句中的?占位符
*
* 注意: query方法返回值,返回的是T 泛型, 具体返回值类型,跟随结果集处理方式变化
*/
public class QueryRunnerSelectDemo {
private static Connection con = C3P0Utils.getConnection();
public static void main(String[] args) throws SQLException{
// arrayHandler();
// arrayListHandler();
// beanHandler();
beanListHander();
// columnListHandler();
// scalarHandler();
// mapHandler();
// mapListHandler();
}
/*
* 结果集第八种处理方法,MapListHandler
* 将结果集每一行存储到Map集合,键:列名,值:数据
* Map集合过多,存储到List集合
*/
public static void mapListHandler()throws SQLException{
QueryRunner qr = new QueryRunner();
String sql = "SELECT * FROM sort";
//调用方法query,传递结果集实现类MapListHandler
//返回值List集合, 存储的是Map集合
List<Map<String,Object>> list = qr.query(con, sql, new MapListHandler());
//遍历集合list
for( Map<String,Object> map : list ){
for(String key : map.keySet()){
System.out.print(key+"..."+map.get(key));
}
System.out.println();
}
}
/*
* 结果集第七种处理方法,MapHandler
* 将结果集第一行数据,封装到Map集合中
* Map<键,值> 键:列名 值:这列的数据
*/
public static void mapHandler()throws SQLException{
QueryRunner qr = new QueryRunner();
String sql = "SELECT * FROM sort";
//调用方法query,传递结果集实现类MapHandler
//返回值: Map集合,Map接口实现类, 泛型
Map<String,Object> map = qr.query(con, sql, new MapHandler());
//遍历Map集合
for(String key : map.keySet()){
System.out.println(key+".."+map.get(key));
}
}
/*
* 结果集第六种处理方法,ScalarHandler
* 对于查询后,只有1个结果(一行一列,聚合函数)
*/
public static void scalarHandler()throws SQLException{
QueryRunner qr = new QueryRunner();
String sql = "SELECT COUNT(*) FROM sort";
//调用方法query,传递结果集处理实现类ScalarHandler
long count = qr.query(con, sql, new ScalarHandler<Long>());
System.out.println(count);
}
/*
* 结果集第五种处理方法,ColumnListHandler
* 结果集,指定列的数据,存储到List集合
* List<Object> 每个列数据类型不同
*/
public static void columnListHandler()throws SQLException{
QueryRunner qr = new QueryRunner();
String sql = "SELECT * FROM sort ";
//调用方法 query,传递结果集实现类ColumnListHandler
//实现类构造方法中,使用字符串的列名
List<Object> list = qr.query(con, sql, new ColumnListHandler<Object>("sname"));
for(Object obj : list){
System.out.println(obj);
}
}
/*
* 结果集第四种处理方法, BeanListHandler
* 结果集每一行数据,封装JavaBean对象
* 多个JavaBean对象,存储到List集合
*/
public static void beanListHander()throws SQLException{
QueryRunner qr = new QueryRunner();
String sql = "SELECT * FROM sort ";
//调用方法query,传递结果集处理实现类BeanListHandler
List<Sort> list = qr.query(con, sql, new BeanListHandler<Sort>(Sort.class));
for(Sort s : list){
System.out.println(s);
}
}
/*
* 结果集第三种处理方法,BeanHandler
* 将结果集的第一行数据,封装成JavaBean对象
* 注意: 被封装成数据到JavaBean对象, Sort类必须有空参数构造
*/
public static void beanHandler()throws SQLException{
QueryRunner qr = new QueryRunner();
String sql = "SELECT * FROM sort ";
//调用方法,传递结果集实现类BeanHandler
//BeanHandler(Class<T> type)
Sort s = qr.query(con, sql, new BeanHandler<Sort>(Sort.class));
System.out.println(s);
}
/*
* 结果集第二种处理方法,ArrayListHandler
* 将结果集的每一行,封装到对象数组中, 出现很多对象数组
* 对象数组存储到List集合
*/
public static void arrayListHandler()throws SQLException{
QueryRunner qr = new QueryRunner();
String sql = "SELECT * FROM sort";
//调用query方法,结果集处理的参数上,传递实现类ArrayListHandler
//方法返回值 每行是一个对象数组,存储到List
List<Object[]> result= qr.query(con, sql, new ArrayListHandler());
//集合的遍历
for( Object[] objs : result){
//遍历对象数组
for(Object obj : objs){
System.out.print(obj+" ");
}
System.out.println();
}
}
/*
* 结果集第一种处理方法, ArrayHandler
* 将结果集的第一行存储到对象数组中 Object[]
*/
public static void arrayHandler()throws SQLException{
QueryRunner qr = new QueryRunner();
String sql = "SELECT * FROM sort";
//调用方法query执行查询,传递连接对象,SQL语句,结果集处理方式的实现类
//返回对象数组
Object[] result = qr.query(con, sql, new ArrayHandler());
for(Object obj : result){
System.out.print(obj);
}
}
}
补充通用的CRUD方法:
package com.commoncrud;
import java.sql.Connection;
import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.junit.Test;
import com.entity.Sort;
import com.util.C3P0Utils;
import com.util.JDBCUtils;
public class CommonCRUDUtils {
@Test
public void testUpdate(){
//update("update sort set sprice = ? where sid = ?" , 999 ,1);
//update02("update sort set sprice = ? where sid = ?" , 999 ,2);
}
//这种update是根据参数个数
public void update(String sql ,Object ... args) {
Connection conn = null;
PreparedStatement ps=null;
try {
conn = C3P0Utils.getConnection();
ps = conn.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
//因为不知道是什么类型的数据,所以都使用setObject来对待。
ps.setObject(i+1, args[i]);
}
ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}finally {
JDBCUtils.close(ps, conn);
}
}
//这种update是根据问号个数
public void update02(String sql ,Object ... args) {
Connection conn = null;
PreparedStatement ps=null;
try {
conn = C3P0Utils.getConnection();
ps = conn.prepareStatement(sql);
//元数据
/*数据库元数据 DatabaseMetaData
参数元数据 ParameterMetaData
结果集元数据 ResultSetMetaData*/
//获取到有几个问号,占位符
ParameterMetaData metaData = ps.getParameterMetaData();
int count = metaData.getParameterCount();
for (int i = 0; i < count; i++) {
//因为不知道是什么类型的数据,所以都使用setObject来对待。
ps.setObject(i+1, args[i]);
}
ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}finally {
JDBCUtils.close(ps, conn);
}
}
@Test
public void testQuery(){
Sort sort = query("select * from sort where sid = ?" ,new ResultSetHandler<Sort>(){
@Override
public Sort handle(ResultSet rs) {
// TODO Auto-generated method stub
try {
Sort sort = new Sort();
if(rs.next()){
String sname = rs.getString("sname");
int sprice = rs.getInt("sprice");
sort.setSname(sname);
sort.setSprice(sprice);
}
return sort;
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
} , 3);
System.out.println(sort);
}
public <T> T query(String sql , ResultSetHandler<T> handler, Object ...args ){
Connection conn = null;
PreparedStatement ps=null;
try {
conn = C3P0Utils.getConnection();
ps = conn.prepareStatement(sql);
//获取到有几个问号,占位符
ParameterMetaData metaData = ps.getParameterMetaData();
int count = metaData.getParameterCount();
for (int i = 0; i < count; i++) {
//因为不知道是什么类型的数据,所以都使用setObject来对待。
ps.setObject(i+1, args[i]);
}
//执行查询工作, 然后得到结果集
ResultSet rs = ps.executeQuery();
//把结果集丢给调用者,让它去封装数据 ,返回封装数据
T t = (T) handler.handle(rs);
return t;
//问题一: 这里的数据获取,以及封装成上面对象返回。不知道。 因为调用的地方需要的数据不同。
/*while(rs.next()){
rs.getInt("id");
rs.getString("name");
// 。。。
}*/
} catch (SQLException e) {
e.printStackTrace();
}finally {
JDBCUtils.close(ps, conn);
}
return null;
}
}
其中ResultSetHandler接口是自定义接口,
package com.commoncrud;
import java.sql.ResultSet;
public interface ResultSetHandler<T> {
T handle(ResultSet rs);
}