0.其它
jdbc.url=jdbc:mysql:///mybatis?useUnicode=true&characterEncoding=UTF8&allowMultiQueries=true
jdbc:mysql:///hib_demo?createDatabaseIfNotExist=true
1.jdbc概述
数据库驱动
JDBC是Java数据库连接技术的简称,提供连接各种常用数据库的能力
SUN公司为了简化、统一对数据库的操作,定义了一套Java操作数据库的规范,称之为JDBC。
Sun公司为简化数据库开发,定义了一套jdbc接口,这套接口由数据库厂商去实现,这样,开发人员只需要学习jdbc接口,并通过jdbc加载具体的驱动,就可以操作数据库。
JDBC全称为:Java Data Base Connectivity(java数据库连接),它主要由接口组成。
组成JDBC的2个包:
java.sql
javax.sql
开发JDBC应用需要以上2个包的支持外,还需要导入相应JDBC的数据库实现(即数据库驱动)。
2.jdbc作用
使用java代码操作数据库
3.jdbc连接模式
因为市面上有很多不同的数据库,那么对于开发人员来说,就要学习不同的数据库驱动来操作不同的数据库。如果数据库更新了,驱动也有可能更新,学习成本太高。
那么有了jdbc以后,开发者就不用学习那么多数据库驱动了,只需要学习jdbc即可。
jdbc是接口,是规范,由数据库厂商去开发具体的实现。
4.jdbc开发步骤
1.导入数据库驱动jar包
2.注册驱动程序
3.获取数据库连接
4.创建Statement对象
5.使用Statement对象来发送sql语句
6.得到结果,处理结果
7.关闭连接
5.mysql使用
//1.导入数据库驱动jar包
// mysql-connector-java-5.1.7-bin.jar
//2.注册驱动程序
Class.forName("com.mysql.jdbc.Driver");
//3.获取数据库连接
String url = "jdbc:mysql://localhost:3306/test";
String username="root";
String password="root";
Connection connection = DriverManager.getConnection(url, username, password);
//4.创建Statement对象
Statement statement = connection.createStatement();
//5.使用Statement对象来发送sql语句
String sql = "create table student(id int primary key auto_increment,name varchar(20),age int)";
int count = statement.executeUpdate(sql);
//6.得到结果,处理结果
System.out.println("影响了"+count+"行");
//7.关闭连接
statement.close();
connection.close();
5.jdbc的详细体系
1.jdbc接口存放位置:java.sql.* javax.sql.*
2.核心接口
DriverManager
用于加载驱动,并创建与数据库的链接
注意:在实际开发中并不推荐采用registerDriver方法注册驱动。原因有二:
一、查看Driver的源代码可以看到,如果采用此种方式,会导致驱动程序注册两次,也就是在内存中会有两个Driver对象。
二、程序依赖mysql的api,脱离mysql的jar包,程序将无法编译,将来程序切换底层数据库将会非常麻烦。
推荐方式:Class.forName(“com.mysql.jdbc.Driver”);
采用此种方式不会导致驱动对象在内存中重复出现,并且采用此种方式,程序仅仅只需要一个字符串,不需要依赖具体的驱动,使程序的灵活性更高。
Driver接口 : 驱动程序接口,使用驱动程序可以连接数据库
如果DriverManager.registerDriver(new com.mysql.jdbc.Driver());这样写等于注册两次。看源码里面有静态代码块。
所以 Class.forName("com.mysql.jdbc.Driver");就是在加载Driver中的静态代码块,这样就等于注册了。
Connection接口 : 代表一个数据库连接
它是非常稀有的资源,用完后必须马上释放,如果Connection不能及时、正确的关闭,极易导致系统宕机。
Connection的使用原则是尽量晚创建,尽量早的释放。
为确保资源释放代码能运行,资源释放代码也一定要放在finally语句中。
Statement接口 : 用来执行静态sql语句
PreparedStatement接口 : 用来执行预编译sql语句
CallableStatement接口 : 用来执行存储过程的sql语句
ResultSet接口 : 代表数据库的结果集
Jdbc程序中的ResultSet用于代表Sql语句的执行结果。Resultset封装执行结果时,采用的类似于表格的方式。
ResultSet 对象维护了一个指向表格数据行的游标,初始的时候,游标在第一行之前,调用ResultSet.next() 方法,
可以使游标指向具体的数据行,进行调用方法获取该行的数据。
6.常用数据类型转换表
7.增加
//1.导入数据库驱动jar包
// mysql-connector-java-5.1.7-bin.jar
//2.注册驱动程序
Class.forName("com.mysql.jdbc.Driver");
//3.获取数据库连接
String url = "jdbc:mysql://localhost:3306/test";
String username="root";
String password="root";
Connection connection = DriverManager.getConnection(url, username, password);
//4.创建Statement对象
Statement statement = connection.createStatement();
//5.使用Statement对象来发送sql语句
String sql = "insert into student(name,age) values ('张三',20)";
int count = statement.executeUpdate(sql);
//6.得到结果,处理结果
System.out.println("影响了"+count+"行");
//7.关闭连接
statement.close();
connection.close();
8.修改
//1.导入数据库驱动jar包
// mysql-connector-java-5.1.7-bin.jar
//2.注册驱动程序
Class.forName("com.mysql.jdbc.Driver");
//3.获取数据库连接
String url = "jdbc:mysql://localhost:3306/test";
String username="root";
String password="root";
Connection connection = DriverManager.getConnection(url, username, password);
//4.创建Statement对象
Statement statement = connection.createStatement();
//5.使用Statement对象来发送sql语句
String sql = "update student set age=30 where id = 1";
int count = statement.executeUpdate(sql);
//6.得到结果,处理结果
System.out.println("影响了"+count+"行");
//7.关闭连接
statement.close();
connection.close();
9.删除
//1.导入数据库驱动jar包
// mysql-connector-java-5.1.7-bin.jar
//2.注册驱动程序
Class.forName("com.mysql.jdbc.Driver");
//3.获取数据库连接
String url = "jdbc:mysql://localhost:3306/test";
String username="root";
String password="root";
Connection connection = DriverManager.getConnection(url, username, password);
//4.创建Statement对象
Statement statement = connection.createStatement();
//5.使用Statement对象来发送sql语句
String sql = "delete from student where id = 1";
int count = statement.executeUpdate(sql);
//6.得到结果,处理结果
System.out.println("影响了"+count+"行");
//7.关闭连接
statement.close();
connection.close();
10.查询
//1.导入数据库驱动jar包
// mysql-connector-java-5.1.7-bin.jar
//2.注册驱动程序
Class.forName("com.mysql.jdbc.Driver");
//3.获取数据库连接
String url = "jdbc:mysql://localhost:3306/test";
String username="root";
String password="root";
Connection connection = DriverManager.getConnection(url, username, password);
//4.创建Statement对象
Statement statement = connection.createStatement();
//5.使用Statement对象来发送sql语句
String sql = "select * from student";
//6.得到结果,处理结果
ResultSet resultSet = statement.executeQuery(sql);
while(resultSet.next()){
int id = resultSet.getInt(1);
String name = resultSet.getString(2);
int age = resultSet.getInt(3);
System.out.println(id+"-"+name+"-"+age);
}
//7.关闭连接
statement.close();
connection.close();
11.数据库分页
Select * from table limit M,N
M:记录开始索引位置,从0开始
N:取多少条记录。
12.jdbc封装
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class JDBCUtil {
private static final String url = "jdbc:mysql://localhost:3306/test";
private static final String username = "root";
private static final String password = "root";
static{
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static Connection getConnection(){
try {
Connection connection = DriverManager.getConnection(url, username, password);
return connection;
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
public static void close(ResultSet resultSet,Statement statement,Connection connection){
if(resultSet != null){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(statement != null){
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(connection != null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
13.PreparedStatement预编译
//?代表一个参数的占位,预编译验证sql是否正确
PreparedStatement prepareStatement = connection.prepareStatement("insert into student(id,name,age) values(?,?,?)");
//给参数赋值
prepareStatement.setInt(1,1);
prepareStatement.setString(2,"shuaige");
prepareStatement.setInt(3, 30);
//把参数发送给数据库
int count = prepareStatement.executeUpdate();
System.out.println("影响了"+count+"行");
14.PreparedStatement和Statement的区别
语法:
Statement对象只支持静态sql,而PreparedStatement即支持静态sql也支持预编译sql
执行效率
PreparedStatement对象比Statement对象执行sql的效率会高。因为PreparedStatement可以利用数据库的sql缓存。
Statement发送sql到数据库
1)校验sql的语法,如果不合法就报错
2)校验sql的执行权限,如果没有权限就报错。
3)执行sql
sql缓存区:先在sql缓存区查询是否已经执行过此sql,如果执行过,则取出对应的执行任务执行,
如果没有执行过,则创建新的执行任务执行,把当前的sql放入缓存区。
PreparedStatement发送sql到数据库
1)先发送sql到数据库
2)发送占位的数据到数据库
以后就只需要发送占位的数据到数据库就可以了。在mysql不明显,在Oracle才明显。
安全问题
Statement对象相对不安全。因为可以被sql注入。
PreparedStatement能够防止sql注入。
结论:尽量使用PreparedStatement。
select * from users where name='shuaige' and password='1234';
select * from users where 1=1;
select * from users where name='随便写' or 1=1 --' and password='1234'
15.执行存储过程CallableStatement
//1.带有输入参数的存储过程
//支持预编译
CallableStatement callableStatement = connection.prepareCall("call pro_test(?)");
callableStatement.setInt(1, 1);
ResultSet resultSet = callableStatement.executeQuery();
//2.带有输出参数的存储过程
//支持预编译
CallableStatement callableStatement = connection.prepareCall("call pro_test1(?)");
//把某个参数注册为输出参数
callableStatement.registerOutParameter(1, java.sql.Types.VARCHAR);
//执行存储过程
callableStatement.executeQuery();
//取出输出参数的值
String text = callableStatement.getString(1);
16.jdbc核心类图
17.url写法
//程序连接数据库的时候用UTF-8编码
?useUnicode=true&characterEncoding=UTF-8
18.读取配置文件获取数据库配置信息
//1.jdbc.properties代码
url=jdbc:mysql://localhost:3306/test
username=root
password=root
driverclass=com.mysql.jdbc.Driver
//2.读取代码
private static String url = null;
private static String username = null;
private static String password = null;
private static String driverclass = null;
static{
Properties prop = new Properties();
//JDBCUtil.class.getResourceAsStream 获得的是当前文件所在的包的src根目录
InputStream inputStream = JDBCUtil.class.getResourceAsStream("/jdbc.properteis");
try {
prop.load(inputStream);
url = prop.getProperty("url");
username = prop.getProperty("username");
password = prop.getProperty("password");
driverclass = prop.getProperty("driverclass");
Class.forName(driverclass);
} catch (Exception e) {
e.printStackTrace();
}
}
19.jdbc批处理
1)作用
一次发送多条sql语句,从而提高数据库执行sql的效率。
2)批处理
Statement
addBatch方法 :添加sql到缓存
excuteBatch方法 :发送缓存的所有sql语句到数据库执行
clearBatch方法 :清空缓存的所有sql语句
PreparedStatement
addBatch方法 : 把一组参数添加到缓存
executeBatch方法 :发送缓存的所有参数到数据库执行
clearBatch方法 :清空缓存的所有参数
3)需求
使用Statement直接插入
使用Statement+批处理插入
使用PreparedStatement直接插入
使用PreparedStatement+批处理插入
结论:Oracle对PreparedStatement和批处理的支持比较好,但是mysql的支持并不明显。
20.jdbc获取自增长的值
//1.导入数据库驱动jar包
// mysql-connector-java-5.1.7-bin.jar
//2.注册驱动程序
Class.forName("com.mysql.jdbc.Driver");
//3.获取数据库连接
String url = "jdbc:mysql://localhost:3306/test";
String username="root";
String password="root";
Connection connection = DriverManager.getConnection(url, username, password);
//4.创建PreparedStatement
String sql = "insert into demp(name) values(?)";
PreparedStatement prepareStatement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
prepareStatement.setString(1,"shuaige");
prepareStatement.executeUpdate();
ResultSet resultSet = prepareStatement.getGeneratedKeys();
int deptId = 0;
if(resultSet.next()){
deptId = resultSet.getInt(1);
}
sql = "insert into user(name,deptno) values(?,?)";
PreparedStatement prepareStatement2 = connection.prepareStatement(sql);
prepareStatement2.setString(1, "shuaige");
prepareStatement2.setInt(2,deptId);
prepareStatement2.executeUpdate();
21.jdbc处理文件(字符和字节)数据
1)分类
字符文件:txt html xml
mysql : tinytext text(64k) mediumtext(16M) longtext(4G)
oracle : clob
字节文件:二进制,图片,视频,zip。。
mysql : tinyblob blob(64k) mediumblob(16M) longblob(4G)
oracle : blob
2)写入字符文件的代码
//1.导入数据库驱动jar包
// mysql-connector-java-5.1.7-bin.jar
//2.注册驱动程序
Class.forName("com.mysql.jdbc.Driver");
//3.获取数据库连接
String url = "jdbc:mysql://localhost:3306/test";
String username="root";
String password="root";
Connection connection = DriverManager.getConnection(url, username, password);
//4.创建PreparedStatement
String sql = "insert into article(name,content) values(?,?)";
PreparedStatement prepareStatement = connection.prepareStatement(sql);
File file = new File("D:\\test.txt");
FileReader reader = new FileReader(file);
prepareStatement.setString(1,file.getName());
prepareStatement.setClob(2, reader);
prepareStatement.executeUpdate();
3)读取字符文件的代码
//1.导入数据库驱动jar包
// mysql-connector-java-5.1.7-bin.jar
//2.注册驱动程序
Class.forName("com.mysql.jdbc.Driver");
//3.获取数据库连接
String url = "jdbc:mysql://localhost:3306/test";
String username="root";
String password="root";
Connection connection = DriverManager.getConnection(url, username, password);
//4.创建PreparedStatement
String sql = "select name,content from article where id = 1";
PreparedStatement prepareStatement = connection.prepareStatement(sql);
ResultSet resultSet = prepareStatement.executeQuery();
if(resultSet.next()){
Clob clob = resultSet.getClob("content");
Reader reader = clob.getCharacterStream();
//写出到硬盘即可
}
4)写入字节文件的代码
//1.导入数据库驱动jar包
// mysql-connector-java-5.1.7-bin.jar
//2.注册驱动程序
Class.forName("com.mysql.jdbc.Driver");
//3.获取数据库连接
String url = "jdbc:mysql://localhost:3306/test";
String username="root";
String password="root";
Connection connection = DriverManager.getConnection(url, username, password);
//4.创建PreparedStatement
String sql = "insert into images(content) values(?)";
PreparedStatement prepareStatement = connection.prepareStatement(sql);
File file = new File("D:\\shuaige.png");
FileInputStream input = new FileInputStream(file);
prepareStatement.setBlob(1, input);
prepareStatement.executeUpdate();
5)读取字节文件的代码
//1.导入数据库驱动jar包
// mysql-connector-java-5.1.7-bin.jar
//2.注册驱动程序
Class.forName("com.mysql.jdbc.Driver");
//3.获取数据库连接
String url = "jdbc:mysql://localhost:3306/test";
String username="root";
String password="root";
Connection connection = DriverManager.getConnection(url, username, password);
//4.创建PreparedStatement
String sql = "select content from images where id = 1";
PreparedStatement prepareStatement = connection.prepareStatement(sql);
ResultSet resultSet = prepareStatement.executeQuery();
if(resultSet.next()){
Blob blob = resultSet.getBlob(1);
InputStream inputStream = blob.getBinaryStream();
//写出到硬盘
}
22.jdbc时间处理
//1.导入数据库驱动jar包
// mysql-connector-java-5.1.7-bin.jar
//2.注册驱动程序
Class.forName("com.mysql.jdbc.Driver");
//3.获取数据库连接
String url = "jdbc:mysql://localhost:3306/test";
String username="root";
String password="root";
Connection connection = DriverManager.getConnection(url, username, password);
//4.创建PreparedStatement
String sql = "insert into timetest(logintime) values(?)";
PreparedStatement prepareStatement = connection.prepareStatement(sql);
prepareStatement.setDate(1, new java.sql.Date(new java.util.Date().getTime()));
prepareStatement.setTimestamp(1, new Timestamp(new java.util.Date().getTime()));
prepareStatement.executeUpdate();
23.jdbc事务
1)概念
如果把(多条)sql语句看做是一个事务,那么这个事务里面的操作要么一起成功过,要么一起失败。
2)数据库的事务功能
数据库的类型innoDB才能完全支持事务。
3)数据库的事务命令
set autocommit=0; 开始事务(关闭自动提交事务的功能。默认值1,自动提交事务 0,关闭自动提交)
commit 提交事务,一旦事务提交了,就不能回滚了。
rollback 回滚事务。回滚到事务的开始位置。
3)sql命令
set autocommit=0;
4)代码
//1.导入数据库驱动jar包
// mysql-connector-java-5.1.7-bin.jar
//2.注册驱动程序
Class.forName("com.mysql.jdbc.Driver");
//3.获取数据库连接
String url = "jdbc:mysql://localhost:3306/test";
String username="root";
String password="root";
Connection connection = DriverManager.getConnection(url, username, password);
//4.创建PreparedStatement
String sql = "update account set money = money-1000 where name='jack'";
String sql1 = "update account set money = money+1000 where name='rose'";
connection.setAutoCommit(false);
PreparedStatement prepareStatement = connection.prepareStatement(sql);
prepareStatement.executeUpdate();
//报错
PreparedStatement prepareStatement2 = connection.prepareStatement(sql1);
prepareStatement2.executeUpdate();
connection.commit();
//异常中回滚 rollback();
5)事务使用的场景
一个业务操作里面存在多条的更新类语句(DML语句)
6)事务的四个特征
原子性 (A)Atomicity
事务是一个不可分割的单元,要么一起成功,要么一起失败
一致性 (C)Consistency
事务必须使数据库从一个一致性状态变换到另外一个一致性状态
隔离性 (I)Isolation
多个事务并发操作之间应该要相互隔离的
如果多个事务之间没有隔离,则会产生以下现象:
1)脏读
一个事务读到了另一个事务未提交的更新数据
2)不可重复读
一个事务读到了另一个事务已经更新的数据
3)幻读
一个事务读到了另一个事务已经提交的插入数据
数据库的4个隔离级别来防止以上现象
serializable 最高级别 不支持脏读,不可重复读,幻读
repeatable read不支持脏读,不可重复读.支持幻读(mysql默认)
read committed 不支持脏读.支持不可重复读,幻读(Oracle默认)
read uncommitted 最低级别 支持脏读,不可重复读,幻读
mysql查看事务的隔离级别
select @@global.tx_isolation;(全局)
select @@tx_isolation;(会话)
mysql修改事务的隔离级别
set global transaction isolation level read committed;(全局)
set session transaction isolation level uncommitted;(会话)
数据库隔离级别越高,安全性越高,数据库并发性能越低。
持久性 (D)Durability
事务一旦提交,数据永远保存下来
24.连接优化
为什么需要连接池
访问前需要先获取连接
每次操作结束后,要释放资源
频繁的连接导致系统的安全性和稳定性差
工作原理
连接池是由容器提供的,用来管理池中连接对象
连接池自动分配连接对象并对闲置的连接进行回收
1)作用
1.提高Connection对象的利用率(一个Connection对象可以多次操作)
2.控制Connection对象的数量,保证数据库服务器的安全
引入连接池的概念来实现以上的功能
2)连接池
sun设计了一个接口 javax.sql.DataSource 是所有连接池实现的接口
getConnection 从连接池中获取连接的方法
25.jdbc优化-自定义连接池
public class MyDataSource implements DataSource {
public static LinkedList<Connection> pool = new LinkedList<Connection>();
private int initCount = 5;
private int maxCount = 10;
private int currentCount=0;
private static String url = "jdbc:mysql://localhost:3306/test";
private static String user = "root";
private static String password = "root";
private static String driverClass = "com.mysql.jdbc.Driver";
static{
try {
Class.forName(driverClass);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private static Connection createConnection(){
try {
return DriverManager.getConnection(url, user, password);
} catch (SQLException e) {
}
return null;
}
public MyDataSource() {
for(int i = 0;i<initCount;i++){
pool.add(createConnection());
currentCount++;
}
}
@Override
public Connection getConnection() throws SQLException {
if(pool.size()>0){
//从池中取出并且返回一个连接
return pool.removeFirst();
}else{
if(currentCount<maxCount){
currentCount++;
return createConnection();
}
throw new RuntimeException("超出最大连接数");
}
}
public static void release(Connection conn){
pool.addLast(conn);
}
}
26.jdbc优化-自定义连接池优化-代理
代理模式的要数
代理接口:代理类和被代理类共同的行为
被代理类:需要被代理的类
代理类:和被代理类有相同的接口,代理类可以有自己的实现
静态代理和动态代理
27.jdbc优化-自定义连接池优化-静态代理
特点是代理类是由开发者自行编写
步骤:
1实现和被代理类相同的接口
2声明一个接口成员变量
3在代理类的构造方法中接收被代理类实例
4在代理类的方法中实现自己的逻辑
处理自定义连接池的close方法
public class MyDataSource implements DataSource {
public static LinkedList<Connection> pool = new LinkedList<Connection>();
private int initCount = 5;
private int maxCount = 10;
private int currentCount=0;
private static String url = "jdbc:mysql://localhost:3306/test";
private static String user = "root";
private static String password = "root";
private static String driverClass = "com.mysql.jdbc.Driver";
static{
try {
Class.forName(driverClass);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private Connection createConnection(){
try {
Connection connection = DriverManager.getConnection(url, user, password);
Connection proxy = new MyConnectionProxy(connection, this);
return proxy;
} catch (SQLException e) {
}
return null;
}
public MyDataSource() {
for(int i = 0;i<initCount;i++){
pool.add(createConnection());
currentCount++;
}
}
@Override
public Connection getConnection() throws SQLException {
if(pool.size()>0){
//从池中取出并且返回一个连接
return pool.removeFirst();
}else{
if(currentCount<maxCount){
currentCount++;
return createConnection();
}
throw new RuntimeException("超出最大连接数");
}
}
public static void release(Connection conn){
pool.addLast(conn);
}
}
public class MyConnectionProxy implements Connection{
private Connection realConn;
private MyDataSource datasource;
public MyConnectionProxy(Connection realConn,MyDataSource datasource){
this.realConn = realConn;
this.datasource = datasource;
}
@Override
public Statement createStatement() throws SQLException {
return realConn.createStatement();
}
@Override
public void close() throws SQLException {
datasource.pool.addLast(this);
}
}
28.jdbc优化-自定义连接池优化-动态代理
特点是代理类是由程序生成的
public class MyDataSource implements DataSource {
public static LinkedList<Connection> pool = new LinkedList<Connection>();
private int initCount = 5;
private int maxCount = 10;
private int currentCount=0;
private static String url = "jdbc:mysql://localhost:3306/test";
private static String user = "root";
private static String password = "root";
private static String driverClass = "com.mysql.jdbc.Driver";
static{
try {
Class.forName(driverClass);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private Connection createConnection(){
try {
final Connection connection = DriverManager.getConnection(url, user, password);
Connection proxy = (Connection)Proxy.newProxyInstance(this.getClass().getClassLoader(),
new Class[]{Connection.class},
new InvocationHandler() {
@Override
public Object invoke(Object newproxy, Method method, Object[] args) throws Throwable {
String name = method.getName();
if("close".equals(name)){
pool.addLast((Connection)newproxy);
}else{
return method.invoke(connection, args);
}
return null;
}
});
return proxy;
} catch (SQLException e) {
}
return null;
}
public MyDataSource() {
for(int i = 0;i<initCount;i++){
pool.add(createConnection());
currentCount++;
}
}
@Override
public Connection getConnection() throws SQLException {
if(pool.size()>0){
//从池中取出并且返回一个连接
return pool.removeFirst();
}else{
if(currentCount<maxCount){
currentCount++;
return createConnection();
}
throw new RuntimeException("超出最大连接数");
}
}
public static void release(Connection conn){
pool.addLast(conn);
}
}
29.jdbc优化-dbcp连接池
1)概述
DBCP是Apache组织的工具,DataBase Connection Pool
是Apache上的一个 java连接池项目,也是 tomcat使用的连接池组件
下载 :
http://commons.apache.org/proper/commons-dbcp/download_dbcp.cgi
http://commons.apache.org/proper/commons-pool/download_pool.cgi
http://commons.apache.org/proper/commons-logging/download_logging.cgi
2)使用步骤
导入jar包
创建连接池
配置连接池参数
获取连接
3)1.4版本代码一
//测试类
public class Test {
public static void main(String[] args) throws Exception {
for(int i = 0;i<11;i++){
Connection connection = DBCPUtil.getConnection();
System.out.println(connection);
//释放连接
connection.close();
}
}
}
//工具类
//commons-dbcp-1.4.jar/commons-pool-1.6.jar
public class DBCPUtil {
private static String url = "jdbc:mysql://localhost:3306/test";
private static String user = "root";
private static String password = "root";
private static String driverClass = "com.mysql.jdbc.Driver";
public static BasicDataSource datasource =null;
static {
datasource = new BasicDataSource();
datasource.setUrl(url);
datasource.setUsername(user);
datasource.setPassword(password);
datasource.setDriverClassName(driverClass);
// 连接池参数配置
// 初始化连接数
datasource.setInitialSize(5);
// 最大连接数
datasource.setMaxActive(10);
// 当超过最大连接后,最长等待时间
datasource.setMaxWait(3000);
}
public static Connection getConnection(){
try {
return datasource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
}
//1.4版本代码二
//dbcp.properties
url=jdbc:mysql://localhost:3306/test
username=root
password=root
driverClassName=com.mysql.jdbc.Driver
initialSize=5
maxActive=10
maxWait=3000
//commons-dbcp-1.4.jar/commons-pool-1.6.jar
public class DBCPUtil {
public static BasicDataSource datasource =null;
static {
Properties prop = new Properties();
InputStream inputStream = DBCPUtil.class.getResourceAsStream("/dbcp.properties");
try {
prop.load(inputStream);
datasource = (BasicDataSource) BasicDataSourceFactory.createDataSource(prop);
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConnection(){
try {
return datasource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
}
public class Test {
public static void main(String[] args) throws Exception {
for(int i = 0;i<11;i++){
Connection connection = DBCPUtil.getConnection();
System.out.println(connection);
//释放连接
connection.close();
}
}
}
4)2.2.1版本代码一
//dbcp.properties
url=jdbc:mysql://localhost:3306/test
username=root
password=root
driverClassName=com.mysql.jdbc.Driver
initialSize=5
maxActive=10
maxWait=3000
//commons-dbcp2-2.1.1.jar/commons-pool2-2.4.2.jar/commons-logging-1.2.jar
public class DBCPUtil {
public static BasicDataSource datasource =null;
static {
Properties prop = new Properties();
InputStream inputStream = DBCPUtil.class.getResourceAsStream("/dbcp.properties");
try {
prop.load(inputStream);
datasource = (BasicDataSource) BasicDataSourceFactory.createDataSource(prop);
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConnection(){
try {
return datasource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
}
public class Test {
public static void main(String[] args) throws Exception {
for(int i = 0;i<11;i++){
Connection connection = DBCPUtil.getConnection();
System.out.println(connection);
//释放连接
connection.close();
}
}
}
//2.2.1代码二
//commons-dbcp2-2.1.1.jar/commons-pool2-2.4.2.jar/commons-logging-1.2.jar
public class DBCPUtil {
private static String url = "jdbc:mysql://localhost:3306/test";
private static String user = "root";
private static String password = "root";
private static String driverClass = "com.mysql.jdbc.Driver";
public static BasicDataSource datasource =null;
static {
try {
datasource = new BasicDataSource();
datasource.setUrl(url);
datasource.setUsername(user);
datasource.setPassword(password);
datasource.setDriverClassName(driverClass);
datasource.setInitialSize(5);
datasource.setMaxTotal(10);
datasource.setMaxWaitMillis(3000);
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConnection(){
try {
return datasource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
}
public class Test {
public static void main(String[] args) throws Exception {
for(int i = 0;i<11;i++){
Connection connection = DBCPUtil.getConnection();
System.out.println(connection);
//释放连接
connection.close();
}
}
}
jdbc优化-c3p0连接池
1)概述
目前应用广泛的连接池工具之一。hibernate框架推荐使用的连接池
C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。
c3p0是异步操作的,缓慢的JDBC操作通过帮助进程完成。
扩展这些操作可以有效的提升性能。
目前使用它的开源项目有Hibernate,Spring等。
c3p0有自动回收空闲连接功能
下载
https://sourceforge.net/projects/c3p0/
2)使用步骤
导入jar包
核心类 ComboPooledDataSource
3)代码一
//c3p0-0.9.5.2.jar/mchange-commons-java-0.2.11.jar
public class C3P0Util {
private static String url = "jdbc:mysql://localhost:3306/test";
private static String user = "root";
private static String password = "root";
private static String driverClass = "com.mysql.jdbc.Driver";
private static ComboPooledDataSource ds = null;
static {
ds = new ComboPooledDataSource();
ds.setJdbcUrl(url);
ds.setUser(user);
ds.setPassword(password);
try {
ds.setDriverClass(driverClass);
} catch (PropertyVetoException e) {
e.printStackTrace();
}
ds.setInitialPoolSize(5);
ds.setMaxPoolSize(10);
ds.setCheckoutTimeout(3000);
}
public static Connection getConnection(){
try {
return ds.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
}
public class Test {
public static void main(String[] args) throws Exception {
for(int i = 0;i<11;i++){
Connection connection = C3P0Util.getConnection();
System.out.println(connection);
connection.close();
}
}
}
4)代码二
// c3p0-config.xml
<c3p0-config>
<!-- 默认配置 -->
<default-config>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/test</property>
<property name="user">root</property>
<property name="password">root</property>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="initialPoolSize">5</property>
<property name="maxPoolSize">10</property>
<property name="checkoutTimeout">3000</property>
</default-config>
<named-config name="intergalactoApp">
<property name="jdbcUrl">jdbc:mysql://localhost:3306/test</property>
<property name="user">root</property>
<property name="password">root</property>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="initialPoolSize">5</property>
<property name="maxPoolSize">10</property>
<property name="checkoutTimeout">3000</property>
</named-config>
</c3p0-config>
//c3p0-0.9.5.2.jar/mchange-commons-java-0.2.11.jar
public class C3P0Util {
private static ComboPooledDataSource ds = null;
static {
//读取默认配置default-config
ds = new ComboPooledDataSource();
//读取配置 named-config = intergalactoApp
//ds = new ComboPooledDataSource("intergalactoApp");
}
public static Connection getConnection(){
try {
return ds.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
}
public class Test {
public static void main(String[] args) throws Exception {
for(int i = 0;i<11;i++){
Connection connection = C3P0Util.getConnection();
System.out.println(connection);
connection.close();
}
}
}
获取数据库元数据 DatabaseMetaData 得到数据库相关信息
DatabaseMetaData dbmd = connection.getMetaData();
System.out.println(dbmd.getDatabaseProductName());
System.out.println(dbmd.getDatabaseMajorVersion());
System.out.println(dbmd.getDatabaseMinorVersion());
System.out.println(dbmd.getDriverName());
System.out.println(dbmd.getDriverMajorVersion());
System.out.println(dbmd.getDriverMinorVersion());
参数元数据 ParameterMetaData 得到参数相关信息
结果集元数据 ResultSetMetaData 得到结果集相关信息
jdbc优化-Proxool连接池
Proxool是一种Java数据库连接池技术。
是sourceforge下的一个开源项目,这个项目提供一个健壮、易用的连接池,
最为关键的是这个连接池提供监控的功能,方便易用,便于发现连接泄漏的情况
jdbc优化-druid连接池
Tomcat中配置数据源连接池
在Tomcat中配置数据源
在Tomcat中的conf/context.xml配置中的<Context></Context>元素下添加
<Resource name="jdbc/shuaige" auth="Container" type="javax.sql.DataSource"
maxActive="100" maxIdle="30" maxWait="10000" username="root"
password="root" driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/shuaige" />
解释
name:数据源名称
type:指定管理者及数据源的类型
maxActive:最大连接数
maxWait:最长等待时间
url:数据库路径
在项目中读取数据源-JNDI读取数据源
JNDI是Java命名与目录接口
通过名称与对象的绑定实现资源获取
代码:
//初始化上下文
Context cxt=new InitialContext();
//获取与逻辑名相关联的数据源对象
//通过Context接口的lookup()查找数据源
//java:comp/env为JavaEE默认路径
//jdbc/news为DataSource的名称
DataSource ds=(DataSource)cxt.lookup("java:comp/env/jdbc/shuaige");
conn=ds.getConnection();
DBUtils工具(jdbc的简单封装)
概述
Apache组织的一个框架,是对jdbc简单的封装
下载
http://commons.apache.org/proper/commons-dbutils/download_dbutils.cgi
使用步骤
导入jar包
使用c3p0连接池 c3p0-0.9.5.2.jar/mchange-commons-java-0.2.11.jar
commons-dbutils-1.7.jar
核心类
QueryRunner
增加数据
public static void insert() throws Exception{
QueryRunner qr = new QueryRunner(C3P0Util.getDataSource());
String sql = "insert into user (name,age) values(?,?)";
Integer id = qr.insert(sql, new ResultSetHandler<Integer>() {
@Override
public Integer handle(ResultSet resultSet) throws SQLException {
resultSet.next();
return resultSet.getInt(1);
}
},new Object[]{"shuaige",32});
System.out.println("返回的新id是:"+id);
}
更新数据
public static void update() throws Exception{
QueryRunner qr = new QueryRunner(C3P0Util.getDataSource());
String sql = "update user set name = ? where id = ?";
int count = qr.update(sql, new Object[]{"shuaigege",25});
System.out.println("影响的记录数:"+count);
}
删除数据
public static void delete() throws Exception{
QueryRunner qr = new QueryRunner(C3P0Util.getDataSource());
String sql = "delete from user where id = ?";
int count = qr.execute(sql, new Object[]{1});
System.out.println("影响的记录数:"+count);
}
查询数据
public static void query()throws Exception{
QueryRunner qr = new QueryRunner(C3P0Util.getDataSource());
String sql = "select * from user";
List<User> list = (List<User>)qr.query(sql,new ResultSetHandler(){
@Override
public Object handle(ResultSet resultSet) throws SQLException {
List<User> list = new ArrayList<User>();
while(resultSet.next()){
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
int age = resultSet.getInt("age");
User user = new User(id,name,age);
list.add(user);
}
return list;
}
});
System.out.println(list);
}
//BeanListHandler: 把结果集封装成一个List(javabean)
public static void query()throws Exception{
QueryRunner qr = new QueryRunner(C3P0Util.getDataSource());
String sql = "select * from user";
List<User> list = qr.query(sql,new BeanListHandler(User.class));
System.out.println(list);
}
//BeanHandler: 把结果集封装成一个javabean
public static void query()throws Exception{
QueryRunner qr = new QueryRunner(C3P0Util.getDataSource());
String sql = "select * from user where id = ?";
User user = qr.query(sql,new BeanHandler(User.class),new Object[]{2});
System.out.println(user);
}
//ArrayHandler: 把结果集的第一行封装成一个对象数组
public static void query()throws Exception{
QueryRunner qr = new QueryRunner(C3P0Util.getDataSource());
String sql = "select * from user where id = ?";
Object[] objArr = qr.query(sql,new ArrayHandler(),new Object[]{2});
for(Object obj : objArr){
System.out.println(obj);
}
}
//ArrayListHandler: 把结果集封装成一个List(对象数组)
public static void query()throws Exception{
QueryRunner qr = new QueryRunner(C3P0Util.getDataSource());
String sql = "select * from user where id = ?";
List<Object[]> list = qr.query(sql,new ArrayListHandler(),new Object[]{2});
for(Object[] objArr : list){
for(Object obj :objArr){
System.out.print(obj);
}
System.out.println();
}
}
//ScalarHandler: 处理聚合查询的结果集 count(*) max(xx) min(xxx)
public static void query()throws Exception{
QueryRunner qr = new QueryRunner(C3P0Util.getDataSource());
String sql = "select count(*) from user";
Long count = qr.query(sql,new ScalarHandler());
System.out.println(count);
}
控制事务
public static void useTransaction() throws Exception{
Connection connection = C3P0Util.getConnection();
connection.setAutoCommit(false);
QueryRunner qr = new QueryRunner();
try {
String sql = "delete from user where id = ?";
int count = qr.execute(connection,sql, new Object[]{1});
System.out.println("影响的记录数:"+count);
int a = 1/0;
String sql1 = "update user set name = ? where id = ?";
int count1 = qr.update(connection,sql1, new Object[]{"shuaigege",25});
System.out.println("影响的记录数:"+count1);
connection.commit();
} catch (Exception e) {
e.printStackTrace();
connection.rollback();
}finally {
connection.close();
}
}
其它
持久化
持久化是将程序中的数据在瞬时状态和持久状态间转换的机制
操作:增加、删除、更新、查询
DAO
Data Access Object(数据存取对象)
实现对持久化数据的访问
DAO起着转换器的作用,把实体类转换为数据库中的记录
写法:
接口/实现类/实体类/数据库工具类
分层开发
特点:
每一层都有自己的职责
上一层不用关心下一层的实现细节,上一层通过下一层提供的对外接口来使用其功能
上一层调用下一层的功能,下一层不能调用上一层功能
好处:
各层专注于自己功能的实现,便于提高质量
便于分工协作,提高开发效率
便于代码复用
便于程序扩展
便于提高开发质量、提高开发效率、便于代码复用、便于程序扩展、便于降低代码的耦合性。
分层时应坚持封装性原则和顺序访问原则
不同层之间通过实体类传输数据
ORM
概述
Object Relation Mapping
对象关系映射
R(数据库) M O(java)
表 <-> 类
字段 <-> 属性
一条记录 <-> 一个对象
ORM实现
hibernate/mybatis/自己代码实现
双向关联(一对多)
create table customer(
id int primary key auto_increment,
name varchar(20)
);
create table order(
id int primary key auto_increment,
orderno varchar(20),
c_id int,
constraint order_customer_fk foreign key(c_id) references customer(id)
);
public class Customer{
private int id;
private String name;
private List<Order> orders = new ArrayList<Order>();
}
public class Order{
private int id;
private String orderno;
private Customer customer;
}
public void save(){
//1.保存用户
insert into customer(name) values(?)
//2.如果用户有关联订单,订单也一起保存
for(Order order : customer.getOrders(){
insert into order (orderno,c_id) values(?,?)
}
}
public void query(){
//1.查询用户
select * from customer where id = ?
//2.查询关联订单
select * from order where c_id = ?
}
双向关联(多对多)
create table teacher(
id int primary key auto_increment,
name varchar(20)
);
create table student(
id int primary key auto_increment,
name varchar(20)
);
create table teacher_student(
t_id int,
s_id int,
constraint tea_stu_tea_fk foreign key(t_id) references teacher(id),
constraint tea_stu_stu_fk foreign key(s_id) references student(id)
);
public class Teacher{
private int id;
private String name;
private List<Student> students = new ArrayList<Student>();
}
public class Student{
private int id;
private String name;
private List<Teacher> teachers = new ArrayList<Teacher>();
}
public void save(){
//1.插入老师
insert into teacher(name) values(?);
//2.如果有学生也插入
insert into student(name) values(?);
//3.插入中间表
insert into teacher_student(t_id,s_id) values(?,?)
}
public void query(){
//1.查询老师
select * from teacher where id = ?
//2.关联查询
select s.* from student s,teacher_student ts where s.id = ts.s_id and t_id=?
}
双向关联 一对一
create table person(
id int primary key auto_increment,
name varchar(20)
)
create table idcard(
id int primary key auto_increment,
idno varchar(18),
p_id int unique,
constraint idcard_person_fk foreign key(p_id) references person(id)
)
public class Person{
private int id;
private String name;
private IdCard idCard;
}
public class IdCard{
private int id;
private String idno;
private Person person;
}
public void save(){
//插入公民表
insert into person (name) values(?)
//插入身份证表
insert into idcard (idno,p_id) values(?,?)
}
public void query(){
//查询公民表
select * from person where id = ?
//查询身份证表
select * from idcard where p_id = ?
}