JDBC
JDBC概述
java DataBase Connectivity:java数据库连接,JDBC是接口,驱动是接口的实现,没有驱动将无法完成数据库连接,一般驱动由数据库厂商免费提供自己数据库对应的驱动。
JDBC开发步骤
- 注册驱动
- 获得连接
- 获得语句执行者
- 执行sql语句
- 处理结果
- 释放资源
步骤详解
public class TestSelect{
try{
//1.注册驱动
//得到Driver相关的类或接口的class对象(利用了反射)
Class.forName("com.mysql.jdbc.Driver");
//2.获取连接
//利用Driver以三个参数去获得连接conn,三个参数jdbc:mysql://localhost:3306/web08"代表url,"root"是用户名,"007011"是密码
Connection conn =DriverManager.getConnection("jdbc:mysql://localhost:3306/web08","root","007011");
//3.获得语句执行者
//3.1编写sql语句
//将一些词换成占位符?
String sql ="select * from table_user where uname=? and upassword=?";
//3.2.创建预处理对象pstmt(conn创建)
PreparedStatement pstmt =conn.prepareStatement(sql);
//3.3用pstmt设置参数(替换占位符)
pstmt.setString(1, username);
pstmt.setString(2, upassword);
//4.执行查询操作
ResultSet rs = pstmt.executeQuery();
//5.对结果集进行处理
if(rs.next()){
System.out.println("恭喜您,"+username+",登陆成功!");
System.out.println(sql);
}else{
System.out.println("账户或密码错误!");
}
//6.释放资源
if(rs!=null)rs.close();
if(stmt!=null)stmt.close();
if(conn!=null)conn.close();
}
}
封装工具类
获得连接
返回值是一个Connection conn,不需要传入值
public static Connection getConnection(){
Connection conn =null;
//1.注册驱动
try {
Class.forName("com.mysql.jdbc.Driver");
//2.获取连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/web08","root","007011");
} catch (Exception e) {
e.printStackTrace();
}
return conn;
}
释放资源
传入三个参数中任意个参数
public static void release(ResultSet rs,PreparedStatement pstmt,Connection conn){
if(rs!=null)
try {
rs.close();
} catch (Exception e) {
e.printStackTrace();
}
if(pstmt!=null)
try {
pstmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
if(conn!=null)
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
使用properties配置文件
将第二步“获取连接”封装起来,将四个参数都封装到properties配置文件中
-
properties文件的要求:
- 文件位置:任意建议src下
- 文件名称:任意扩展名为properties
- 文件内容:一行一组数据,格式是key=value
-
创建配置文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/day07_db
jdbc.user=root
jdbc.password=你的密码
加载配置文件
使用JDK提供的工具类ResourceBundle加载properties文件
public class JDBC_UtilsV2 {
private static String driver;
private static String url;
private static String username;
private static String password;
//静态代码块加载配置文件信息
static{
ResourceBundle bundle =ResourceBundle.getBundle("db");//获得工具类ResourceBundle
driver = bundle.getString("driver");
url = bundle.getString("url");
username = bundle.getString("username");
password =bundle.getString("password");
}
public static Connection getConnection(){
//......
}
public static void release(ResultSet rs,PreparedStatement pstmt,Connection conn){
//........
}
}
除了使用ResourceBundle直接加载,还可以使用类加载器ClassLoader加载properties文件获得输入流,再new一个Properties 对象props,用props加载输入流,
public class JDBC_UtilsV2 {
private static String driver;
private static String url;
private static String username;
private static String password;
//静态代码块加载配置文件信息
static{
//1.通过当前类获取器获取类加载器
ClassLoader classLoader =JDBC_UtilsV3.class.getClassLoader();
//2.通过类加载器的方法获得一个输入流
InputStream is = classLoader.getResourceAsStream("db.properties");
//3.创建一个properties对象
Properties props =new Properties();
//4.加载输入流
props.load(is);
//5.获取相关参数的值
driver = props.getProperty("driver");
url = props.getProperty("url");
username = props.getProperty("username");
password =props.getProperty("password");
}
public static Connection getConnection(){
//......
}
public static void release(ResultSet rs,PreparedStatement pstmt,Connection conn){
//........
}
}
使用工具类实例
使用工具类后:
public class TestUtils {
//更新用户信息方法
@Test
public void testUpdateById(){
Connection conn =null;
PreparedStatement pstmt =null;
try {
//1.获取连接
conn =JDBC_UtilsV3.getConnection();
//2.编写sql语句
String sql ="update table_user set upassword=? where uid =?";
//3.获取执行sql语句对象
pstmt =conn.prepareStatement(sql);
//4.设置参数
pstmt.setString(1, "000");
pstmt.setInt(2, 1);
//5.执行更新操作
int row =pstmt.executeUpdate();
if(row>0){
System.out.println("更新成功!");
}else{
System.out.println("更新失败!");
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
//6.释放资源
JDBC_UtilsV3.release(null, pstmt, conn);
}
使用工具类前:
public class TestSelect{
try{
//1.获取连接
conn =JDBC_UtilsV3.getConnection();
//1.注册驱动
//得到Driver相关的类或接口的class对象(利用了反射)
Class.forName("com.mysql.jdbc.Driver");
//2.获取连接
//利用Driver以三个参数去获得连接conn,三个参数jdbc:mysql://localhost:3306/web08"代表url,"root"是用户名,"007011"是密码
Connection conn =DriverManager.getConnection("jdbc:mysql://localhost:3306/web08","root","007011");
//2.编写sql语句
String sql ="update table_user set upassword=? where uid =?";
//3.获得语句执行者
//3.1编写sql语句,将一些词换成占位符?
String sql ="select * from table_user where uname=? and upassword=?";
//3.获取执行sql语句对象
pstmt =conn.prepareStatement(sql);
//3.2.创建预处理对象pstmt(conn创建)
PreparedStatement pstmt =conn.prepareStatement(sql);
//4.设置参数
pstmt.setString(1, "000");
pstmt.setInt(2, 1);
//3.3用pstmt设置参数(替换占位符)
pstmt.setString(1, username);
pstmt.setString(2, upassword);
//5.执行更新操作
int row =pstmt.executeUpdate();
if(row>0){
System.out.println("更新成功!");
}else{
System.out.println("更新失败!");
}
//4.执行查询操作
ResultSet rs = pstmt.executeQuery();
//5.对结果集进行处理
if(rs.next()){
System.out.println("恭喜您,"+username+",登陆成功!");
System.out.println(sql);
}else{
System.out.println("账户或密码错误!");
}
//6.释放资源
if(rs!=null)rs.close();
if(stmt!=null)stmt.close();
if(conn!=null)conn.close();
}
}
使用连接池
概念:
解决频繁“获得连接”和“释放资源”消耗资源的问题,——使用池来管理Connection,不用创建Connection,而是通过池来获取Connection对象。使用完Connection之后以归还池来代替关闭Connection
规范:
Java为数据库提供了公共的接口:javax.sql.DataSource,各个厂商需要让自己的连接池实现这个接口
自定义连接池步骤
- 创建连接池实现(数据源),并实现javax.sql.DataSource,重写其中的getConnectio()方法
- 提供一个集合,用于存放连接,选择LinkedList来便捷的完成移除和添加操作
- 在静态代码块中为连接池初试化5个链接(本案例)
- 调用getConnection()方法,保证当前连接只能提供一个线程使用,所以我们需要将连接先将连接池中移除(避免再提供给其他线程)
- 使用完之后,不执行close()方法,而是将其添加到连接池中而不是直接释放
定义连接池的实现
定义一个连接池
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.LinkedList;
import java.util.logging.Logger;
import javax.sql.DataSource;
import cn.itheima.jdbc.utils.JDBC_UtilsV3;
//1.创建连接池实现(数据源),并实现jDataSource接口
public class MyDataSource implements DataSource{
// 2.创建1个容器用于存储Connection对象
private static LinkedList<Connection> pool =new LinkedList<Connection>();
//3.创建5个链接放到容器中
static{
for(int i=0;i<5;i++){
Connection conn =JDBC_UtilsV3.getConnection();
pool.add(conn);
}
}
//4.调用getConnection()方法,保证当前连接只能提供一个线程使用,所以我们需要将连接先将连接池中移除(避免再提供给其他线程)
@Override
public Connection getConnection() throws SQLException {
Connection conn = null;
//4.1使用前先判断
if(pool.size()==0){
//4.2池子里面没有,我们再创建一些
for(int i=0;i<5;i++){
conn =JDBC_UtilsV3.getConnection();
//这里的JDBC_UtilsV3是调用了之前定义的工具类
pool.add(conn);
}
}
//4.3如果有,从池子里获取一个Connection,先将其从连接池中移除
conn = pool.remove(0);
return conn;
}
// 5. 使用完之后,不执行close()方法,而是将其添加到连接池中而不是直接释放
/*
* 归还连接对象到连接池中
*/
public void backConnection(Connection conn){
pool.add(conn);
}
@Override
public PrintWriter getLogWriter() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@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 void setLogWriter(PrintWriter arg0) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public void setLoginTimeout(int arg0) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public boolean isWrapperFor(Class<?> arg0) throws SQLException {
// TODO Auto-generated method stub
return false;
}
@Override
public <T> T unwrap(Class<T> arg0) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public Connection getConnection(String arg0, String arg1) throws SQLException {
// TODO Auto-generated method stub
return null;
}
}
测试连接池
public void testAddUser(){
Connection conn =null;
PreparedStatement pstmt =null;
//1.创建自定义连接池对象
MyDataSource dataSource =new MyDataSource();
try {
//2.从池子中获取连接
conn =dataSource.getConnection();
//3.创建sql语句
String sql ="insert into table_user values(null,?,?)";
//4.进行操作
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, "aa");
pstmt.setString(2, "bb");
int rows = pstmt.executeUpdate();
//5.查看是否操作成功
if(rows>0){
System.out.println("添加成功!");
}else{
System.out.println("添加失败!");
}
} catch (Exception e) {
throw new RuntimeException(e);
}finally{
//归还连接池
dataSource.backConnection(conn);
}
方法增强
方法增强的总结:
1.子类继承父类
2.装饰者设计模式:新类实现接口,并将接口的实现类作为构造参数传入新类中,再实现新的类方法
3.动态代理:在运行时动态创建代理类,与装饰者类似
需求:
用户调用getConnection()之后,必须使用dataSource.backConnection()方法执行归还,若调用conn.close()则丢失了该conn。所以我们需要复写connection类的close方法。
复写MyDataSource对象
//1.实现同一个接口Connection
public class MyConnection implements Connection {
//3.定义一个变量
private Connection conn;
private LinkedList<Connection> pool;
//2.编写一个构造方法
public MyConnection(Connection conn,LinkedList<Connection> pool){
this.conn =conn;
this.pool =pool;
}
//4.书写需要增强的方法
public void close() throws SQLException{
pool.add(conn);
}
//5.此方法必须覆盖,否则会出现空指针异常
@Override
public PreparedStatement prepareStatement(String sql) throws SQLException {
return conn.prepareStatement(sql);
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
// 。。。。。。
return false;
}
}
使用新的connection实现
public class MyDataSource1 implements DataSource{
// 1.创建1个容器用于存储Connection对象
private static LinkedList<Connection> pool =new LinkedList<Connection>();
//2.创建5个链接放到容器中
static{
for(int i=0;i<5;i++){
Connection conn =JDBC_UtilsV3.getConnection();
//放入池子connection对象已经改造过了
MyConnection myconn =new MyConnection(conn,pool);
pool.add(myconn);
}
}
@Override
public Connection getConnection() throws SQLException {
Connection conn = null;
//3.使用前先判断
if(pool.size()==0){
//4.池子里面没有,我们再创建一些
for(int i=0;i<5;i++){
conn =JDBC_UtilsV3.getConnection();
pool.add(conn);
}
}
//5.如果有,从池子里获取一个连接对象Connection
conn = pool.remove(0);
return conn;
}
/*
* 归还连接对象到连接池中
*/
public void backConnection(Connection conn){
pool.add(conn);
}
C3P0连接池
开源免费的连接池
使用时需要导入jar包、配置文件(C3P0-config.xml)、创建C3P0的核心工具类ComboPoolDataSource的的实例对象
import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class C3P0Utils {
//使用命名itheima(也可以不写)配置
private static ComboPooledDataSource dataSource = new ComboPooledDataSource("itheima");
//获得数据源
public static DataSource getDataSource(){
return dataSource;
}
//获得Connection连接
public static Connection getConnection(){
try {
return dataSource.getConnection();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
DBCP连接池
也是开源的连接池,企业开发中也比较常见,tomcat内置的连接池
导入jar包、配置文件*.properties、编写工具类
public class DBCPUtils {
private static DataSource dataSource;
static{
try{
//1.加载配置文件properties获得输入流
InputStream is =DBCPUtils.class.getClassLoader().getResourceAsStream("db.properties");
//2.new一个Properties处理输入流
Properties props =new Properties();
props.load(is);
//3.使用工具类创建连接池(数据源)
dataSource =BasicDataSourceFactory.createDataSource(props);
}catch(Exception e){
throw new RuntimeException(e);
}
}
//获得数据源
public static DataSource getDataSource(){
return dataSource;
}
//获得Connection连接
public static Connection getConnection(){
try {
return dataSource.getConnection();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
JavaBean组件
就是一个类,常用于封装数据,具有以下特性:
- 需要实现接口:java.io.Serializable,通常偷懒省略
- 提供私有字段:private 类型 字段名
- 提供getter/setter
- 提供无参构造
public class User {
private int uid;
private String uname;
private String upassword;
public User(){
//构造器
}
public int getUid() {
return uid;
}
public void setUid(int uid) {
this.uid = uid;
}
public String getUname() {
return uname;
}
public void setUname(String uname) {
this.uname = uname;
}
public String getUpassword() {
return upassword;
}
public void setUpassword(String upassword) {
this.upassword = upassword;
}
}
使用DBUtils完成CRUD
DBUtils简化了Java执行sql的语句,并将结果封装成对象列表。
C3P0和DBCP是存放了一堆Connection对象的连接池。避免了java每次加载驱动和连接数据库
DBUtils核心功能和核心类
核心功能
- QueryRunner中提供对sql语句操作的API
- ResultHandler接口,用于定义select操作之后怎样处理结果集
- DBUtils,它就是一个工具类,定义了关闭资源与事务处理的方法
核心类
- QueryRunner(DataSource ds),提供数据源(连接池)。DBUtils底层自动维护connection
- update(String sql,Object…params)执行更新数据,返回int值,表示改变了的行数
- query(String sql,ResultSetHandler rsh,Object… params)执行查询,结果存入ResultSetHandler中
ResultSetHandler结果集处理类
实现DBUtils完成CRUD
省去了PreparedStatement pstmt等操作
@Test
public void testAddUsers(){
//添加用户方法
try {
//1.创建核心类QueryRunner
QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
//2.编写SQL语句
String sql ="insert into table_user values(null,?,?)";
//3.为占位符设置值,params里面是更新的参数
Object[] params ={"dd","ee"};
//4.执行添加操作
int rows = qr.update(sql, params);
if(rows>0){
System.out.println("添加成功");
}else{
System.out.println("添加失败");
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void testQueryAll(){
//查询所有的用户
try {
//1.获取核心类queryRunner
QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
//2.编写sql语句
String sql ="select * from table_user";
//3.执行查询操作 BeanListHandler<User>来存放查询结果
List<User> users = qr.query(sql, new BeanListHandler<User>(User.class));
//4.对结果集合进行遍历
// for(User user:users){
// System.out.println(user.getUname()+":"+user.getUpassword());
// }
} catch (SQLException e) {
e.printStackTrace();
}
}