JDBC
1 JDBC概述
JDBC(Java Database Connectivity)是一个独立于特定数据库管理系统、通用的SQL数据库存取和操作的公共接口(一组API)
简单理解为:JDBC,是SUN提供的一套 API,使用这套API可以实现对具体数据库的操作(获取连接、关闭连接、DML、DDL、DCL)
数据库的驱动:数据库厂商针对于JDBC这套接口,提供的具体实现类的集合。
面向接口编程的思想:
-
JDBC是sun公司提供一套用于数据库操作的接口,java程序员只需要面向这套接口编程即可。
-
不同的数据库厂商,需要针对这套接口,提供不同实现。不同的实现的集合,即为不同数据库的驱动。
通过Driver接口获取数据库连接:
准备工作:
Driver是一个接口,数据库厂商必须提供实现的接口,从而获取到数据库连接,可以通过Driver的实现类的对象获取连接
- 加入mysql驱动
- 解压mysql-connector-java-5.1.18.zip
- 在当前目录下新建lib目录
- 把mysql-connector-java-5.1.18-bin.jar复制到lib目录
- 右键->build-path->add build path加载到类路径下
1、创建一个Driver实现类的对象
Driver driver=new com.mysql.jdbc.Driver();
2、准备连接数据库的基本信息,url,user,password
String url="jdbc:mysql://localhost:3306/mybatis";
String user = "root";
String password = "234523";
3、调用DriverManager.getConnection获取连接
Connection connection = DriverManager.getConnection(url, user, password);
4、关闭数据库连接
connection.close();
完整代码:
public class ConnectionTest {
public static void main(String[] args) throws SQLException {
// 1、获取MySQL驱动
Driver driver = new com.mysql.jdbc.Driver();
// 2、加载数据库基本信息
String url="jdbc:mysql://localhost:3306/mybatis";
String user = "root";
String password = "232323";
// 3、建立连接
Connection connection = DriverManager.getConnection(url, user, password);
System.out.println(connection);
// 4、关闭连接
connection.close();
}
}
测试结果:com.mysql.jdbc.JDBC4Connection@ee7d9f1
但是上面这种方式不太理想,对于数据库的信息起不到保护作用!
连接数据库最佳方法:
首先,我们新建一个配置文件名字叫做jdbc.Properties(最好放在src下面,方便引用),里面添加我们连接数据库需要的信息
注意:配置文件中不要加空格,不要加双引号
user=root
password=123123
url=jdbc:mysql://localhost:3306/mybatis
driverClass=com.mysql.jdbc.Driver
完整代码:
public class BestConnection {
public static void main(String[] args) throws IOException, ClassNotFoundException, SQLException {
// 1、获得类加载器
ClassLoader classLoader = BestConnection.class.getClassLoader();
// 2、读取配置文件信息
InputStream is = classLoader.getResourceAsStream("jdbc.properties");
Properties properties = new Properties();
// 3、加载对应的输入流
properties.load(is);
// 4、拿到配置文件的具体值,getProperty里的值必须和配置文件的一致
String user = properties.getProperty("user");
String url = properties.getProperty("url");
String password = properties.getProperty("password");
String driverClass = properties.getProperty("driverClass");
// 5、加载数据库驱动程序
Class.forName(driverClass);
// 6、建立连接
Connection connection = DriverManager.getConnection(url, user, password);
System.out.println(connection);
// 7、关闭连接
connection.close();
}
}
2 JDBCUtils
说明:
为了方便操作,将连接(关闭)数据库封装成一个工具类
public class JDBCUtils {
// 获取数据库连接
public static Connection getConnection() throws Exception {
// 1.读取配置文件中的4个基本信息
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");
Properties pros = new Properties();
pros.load(is);
String user = pros.getProperty("user");
String password = pros.getProperty("password");
String url = pros.getProperty("url");
String driverClass = pros.getProperty("driverClass");
// 2.加载驱动
Class.forName(driverClass);
// 3.获取连接
Connection conn = DriverManager.getConnection(url, user, password);
return conn;
}
// 增删改的关闭方法
public static void closeResource(Connection conn, Statement ps) {
try {
if (ps != null) {
ps.close();
}
if (conn != null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
// 查询的关闭方法
public static void closeResource(Connection conn, Statement ps, ResultSet rs) {
try {
if (rs != null) {
rs.close();
}
if (ps != null) {
ps.close();
}
if (conn != null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
3 使用PreparedStatement操作Blob(图片、音频等大型文件)类型的变量
插入blob类型数据到数据库:
@Test
// 插入图片到数据库
public void insertTest() throws Exception {
Connection conn = JDBCUtils.getConnection();
String sql = "insert into photos(id,photo) values(?,?)";
// 预编译SQL语句
PreparedStatement ps = conn.prepareStatement(sql);
// 填充占位符
ps.setObject(1, 1);
FileInputStream fis = new FileInputStream(new File("src\\com\\zwh\\blob\\3.jpg"));
ps.setObject(2, fis);
// 执行
ps.execute();
// 关闭连接
JDBCUtils.closeResource(conn, ps);
}
查询数据库的Blob数据:
@Test
// 从数据库将图片取出
public void getBlob() {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
InputStream is = null;
FileOutputStream fos = null;
try {
conn = JDBCUtils.getConnection();
String sql = "select id,photo from photos";
ps = conn.prepareStatement(sql);
rs = ps.executeQuery();
if (rs.next()) {
int id = rs.getInt("id");
// 将Blob类型的字段下载下来,以文件的方式保存在本地
Blob photo = rs.getBlob("photo");
is = photo.getBinaryStream();
fos = new FileOutputStream("src\\com\\zwh\\blob\\six.jpg");
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (fos != null) {
fos.close();
}
if (is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
JDBCUtils.closeResource(conn, ps, rs);
}
}
注意:
数据库,默认max_allowed_packet=4M,更根据你文件大小进行增加。
blob类型不够的话,使用longblob可以解决,xxx too long 的问题
开发中使用PreparedStatement替换Statement,Statement存在SQL注入问题
4 数据库事务
事务:
一组逻辑操作单元,使数据从一种状态变换到另一种状态。
一组逻辑操作单元:
一个或多个DML操作。
事务处理的原则:
保证所事务都作为一个工作单元来执行,即使出现了故障,都不能改变这种执行方式。当在一个事务中执行多个操作时,要么所有的事务都被提交(commit),那么这些修改就永久地保存下来;要么数据库管理系统将放弃所作的所有修改,整个事务回滚(rollback)到最初状态。
哪些操作会导致数据的自动提交?
DDL操作一旦执行,都会自动提交。set autocommit = false 对DDL操作失效
DML默认情况下,一旦执行,就会自动提交。我们可以通过set autocommit = false的方式取消DML操作的自动提交。
默认在关闭连接时,会自动的提交数据
代码实现:
@Test
public void testUpdateWithTx() {
Connection conn = null;
try {
conn = JDBCUtils.getConnection();
System.out.println(conn.getAutoCommit());//true
//1.取消数据的自动提交
conn.setAutoCommit(false);
String sql1 = "update user_table set balance = balance - 100 where user = ?";
update(conn,sql1, "AA");
//模拟网络异常
System.out.println(10 / 0);
String sql2 = "update user_table set balance = balance + 100 where user = ?";
update(conn,sql2, "BB");
System.out.println("转账成功");
//2.提交数据
conn.commit();
} catch (Exception e) {
e.printStackTrace();
//3.回滚数据
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
}finally{
//修改其为自动提交数据
//主要针对于使用数据库连接池的使用
try {
conn.setAutoCommit(true);
} catch (SQLException e) {
e.printStackTrace();
}
JDBCUtils.closeResource(conn, null);
}
}
5 通用的增删改查
通用的增删改:
//1、通用的增删改
public static void commonCRD(String sql, Object[] args) throws Exception {
Connection conn = null;
PreparedStatement ps = null;
try {
//1、建立数据库连接
conn = JDBCUtils.getConnection();
//2、预编译sql语句
ps = conn.prepareStatement(sql);
//3、填充占位符并执行
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
ps.execute();
System.out.println("操作成功~");
} catch (SQLException e) {
e.printStackTrace();
} finally {
//4、关闭连接
JDBCUtils.closeResource(conn, ps);
}
}
通用的返回表中所有数据:
//2、针对于不同的表的通用的查询操作,返回表中的所有记录
public static <T> List<T> commSelAll(Class<T> clazz, String sql, Object... args) throws Exception {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
List<T> allStu = null;
try {
//1、建立连接
conn = JDBCUtils.getConnection();
//2、预编译
ps = conn.prepareStatement(sql);
//3、填充占位符
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
//4、执行并且处理结果集
rs = ps.executeQuery();
//5、获取结果集中的元数据:ResultSetMetaData
ResultSetMetaData rsmd = rs.getMetaData();
//6、通过ResultSetMetaData获取结果集中的列数
int columnCount = rsmd.getColumnCount();
//7、创建集合对象
allStu = new ArrayList<>();
while (rs.next()) {
T t = clazz.newInstance();
//8、处理结果集一行数据中的每一个列:给t对象指定的属性赋值
for (int i = 0; i < columnCount; i++) {
// 9、获取列值
Object columValue = rs.getObject(i + 1);
// 10、获取每个列的列名
String columnName = rsmd.getColumnClassName(i + 1);
String columnLabel = rsmd.getColumnLabel(i + 1);
//11、给t对象指定的columnName属性,赋值为columValue:通过反射
Field field = clazz.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(t, columValue);
}
allStu.add(t);
}
} catch (SQLException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} finally {
//4、关闭
JDBCUtils.closeResource(conn, ps, rs);
}
return allStu;
}
通用的单条查询:
//3、针对于不同的表的通用的查询操作,返回表中的一条记录
public <T> T getInstance(Class<T> clazz, String sql, Object... args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JDBCUtils.getConnection();
ps = conn.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
rs = ps.executeQuery();
// 获取结果集的元数据 :ResultSetMetaData
ResultSetMetaData rsmd = rs.getMetaData();
// 通过ResultSetMetaData获取结果集中的列数
int columnCount = rsmd.getColumnCount();
if (rs.next()) {
T t = clazz.newInstance();
// 处理结果集一行数据中的每一个列
for (int i = 0; i < columnCount; i++) {
// 获取列值
Object columValue = rs.getObject(i + 1);
// 获取每个列的列名
// String columnName = rsmd.getColumnName(i + 1);
String columnLabel = rsmd.getColumnLabel(i + 1);
// 给t对象指定的columnName属性,赋值为columValue:通过反射
Field field = clazz.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(t, columValue);
}
return t;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn, ps, rs);
}
return null;
}
6 数据库连接池
数据库:
在每一次请求数据库都要经历上述过程,创建连接和释放资源也都是些重复性的动作,当请求量比较大时,资源是个很大的浪费。
假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大的浪费数据库的资源,并且极易造成数据库服务器内存溢出、拓机。
连接池:
数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;
释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。
几种常用的数据库连接池:
1、DBCP(Database Connection Pool)
是一个依赖Jakarta commons-pool对象池机制的数据库连接池,Tomcat的数据源使用的就是DBCP。
2、C3P0
是一个开放源代码的JDBC连接池,它在lib目录中与Hibernate一起发布,包括了实现jdbc3和jdbc2扩展规范说明的Connection 和Statement 池的DataSources 对象,sql执行效率较低。
3、Proxool
是一个Java SQL Driver驱动程序,提供了对你选择的其它类型的驱动程序的连接池封装。可以非常简单的移植到现存的代码中。完全可配置。快速,成熟,健壮。可以透明地为你现存的JDBC驱动程序增加连接池功能,在激烈并发时会抛异常,完全不靠谱。
4、BoneCP
是一个开源的快速的 JDBC 连接池。BoneCP很小,只有四十几K
5、Druid
是阿里提供的数据库连接池,据说是集DBCP、C3P0、Proxool优点于一身的连接池,性能最好的数据库连接池
6.1 DBCP数据库连接池
准备工作 :
在项目src目录下创建dbcpconfig.properties配置文件
需要引用的包 commons-dbcp-1.4.jar commons-pool-1.6.jar
#连接设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&useSSL=true
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=utf8
#指定由连接池所创建的连接的自动提交(auto-commit)状态。
defaultAutoCommit=true
#driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。
#可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=READ_UNCOMMITTED
通过DBCP封装连接数据库的方法:
public class DBCPUtils {
//创建一个DBCP数据库连接池
private static DataSource dataSource;
static {
try {
Properties properties = new Properties();
FileInputStream fis = new FileInputStream(new File("src\\com\\zwh\\dbcp\\dbcp.properties"));
properties.load(fis);
// 创建数据源 工厂模式
dataSource = BasicDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
// 获取连接
public static Connection getCollection() throws SQLException {
return dataSource.getConnection(); //从数据源中获取数据
}
// 释放连接
public static void release(Connection connection, PreparedStatement ps, ResultSet rs) {
try {
if (rs != null) {
rs.close();
}
if (ps != null) {
ps.close();
}
if (connection != null) {
connection.close();
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
6.2 C3P0数据库连接池
准备工作 :
在项目src目录下创建c3p0-config.xml配置文件
导入c3p0的包
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<!-- c3p0的缺省设置
如果在代码中 ComboPooledDataSource ds = new ComboPooledDataSource() 这样表示的使用c3p0的默认设置
-->
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbc</property>
<property name="user">root</property>
<property name="password">java</property>
<property name="acquireIncrement">5</property>
<property name="initialPoolSize">10</property>
<property name="maxPoolSize">20</property>
<property name="minPoolSize">5</property>
</default-config>
<!-- c3p0的命名设置
如果在代码中 ComboPooledDataSource ds = new ComboPooledDataSource("mysql") 这样表示的使用c3p0的命名设置
-->
<named-config name="mysql">
<!-- 提供获取连接的4个基本信息 -->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/mybatis</property>
<property name="user">root</property>
<property name="password">2132456</property>
<!-- 进行数据库连接池管理的基本信息 -->
<!-- 当数据库连接池中的连接数不够时,c3p0一次性向数据库服务器申请的连接数 -->
<property name="acquireIncrement">5</property>
<!-- c3p0数据库连接池中初始化时的连接数 -->
<property name="initialPoolSize">10</property>
<!-- c3p0数据库连接池维护的最少连接数 -->
<property name="minPoolSize">10</property>
<!-- c3p0数据库连接池维护的最多的连接数 -->
<property name="maxPoolSize">100</property>
<!-- c3p0数据库连接池最多维护的Statement的个数 -->
<property name="maxStatements">50</property>
<!-- 每个连接中可以最多使用的Statement的个数 -->
<property name="maxStatementsPerConnection">2</property>
</named-config>
</c3p0-config>
通过C3P0封装连接数据库的方法:
public class C3P0Utils {
private static ComboPooledDataSource cpds = new ComboPooledDataSource("mysql");
public static Connection getConnection() throws SQLException {
Connection conn = cpds.getConnection();
return conn;
}
public static void main(String[] args) throws SQLException {
Connection connection = getConnection();
System.out.println(connection);
}
}
6.3 Druid数据库连接池
准备工作:
导入Druid的jar包
创建druid.properties:
url=jdbc:mysql://localhost:3306/mybatis
username=root
password=123452
driverClassName=com.mysql.jdbc.Driver
initialSize=10
maxActive=10
测试代码:
public class DruidUtils {
private static DataSource source;
static {
try {
Properties pros = new Properties();
FileInputStream fis = new FileInputStream(new File("src\\com\\zwh\\druid\\druid.properties"));
pros.load(fis);
source = DruidDataSourceFactory.createDataSource(pros);
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConnection() throws SQLException {
Connection conn = source.getConnection();
return conn;
}
}
3380

被折叠的 条评论
为什么被折叠?



