JDBC的成长过程
概要
1,原生JDBC的6个过程
2,自定义的JDBCUtils工具类(初级形态)
1)解决原生JDBC存在的重复操作--封装数据库连接对象 2)存在的缺点: 3)进行改进: 读取配置文件方式
3,Dbutils工具类—封装了常用的crud操作
常用3个类:
1)QueryRunner,实现增删改查操作 2)ResultHandler,专门用于处理结果集的 主要分8个类: 3)DbUtils,封装了各种关流的方法
4,连接池技术: DBCP, c3p0
两者比较
- 5,Dbutils结合连接池技术
- 6,JDBC在以上存在的5个问题–引入Mybatis框架
- 7,Mybatis的简单实现步骤,优势
正文
1,原生JDBC的6个过程
1)注册驱动
因为sun公司定义了具体连接数据库的接口(即规范),各大数据库厂商具体根据自己的数据库特性编写实现类,来实现该接口,因为程序开发者无需关注具体的实现,只需要了解sun公司定义的这些的接口以及这些接口中的方法的作用,在进行数据库连接操作时,通过接口调用的这些方法就可以执行数据库的操作(多态原理,父类调用方法,实现动态绑定),因而在连接时,需要指明是要连接到哪个数据库,就需要注册驱动(需要导入数据库的驱动包)
在这里以mysql为例
mysql-connector-java-5.1.39-bin.jar
* 注册驱动用2种方式:
1)DriverManager.registerDriver(new Driver());
//查看源代码,Driver类中默认有一个静态代码块,会调用相同方法注册驱动一次,则注册2次驱动程序, 浪费资源 ,不推荐使用
2)Class.forName("com.mysql.jdbc.Driver");
//既然静态代码中会注册,于是直接利用反射技术,加载驱动类字节码文件到内存,静态代码块自动实现注册驱动
2)获取连接对象
Connection con= DriverManager.getConnection(url, user, password);
这里 url:以mysql为例,连接本地主机localhost或127.0.0.1,
规则 url: jdbc:mysql://主机IP:端口号/数据库名
user,password分别为要连接的数据库账号和密码
3)获取SQL语句的执行者对象
这里也称SQl的容器对象,主要有3种(都是接口):
a,Statement
b,PreparedStatement
c,CallableStatement
其中Statement有Sql注入危险,而且执行效率较低, PreparedStatement因为有预编译的功能,可以防止sql注入,且因有缓存,执行效率较高,
CallableStatement主要是对存储过程和函数的操作
Statement,PreparedStatement,CallableStatement 三者是爷,子,孙关系
4)执行者对象调用方法发送SQL语句到数据库
主要是两类方法
executeUpdate:执行增删改操作,返回int,指数据库中受影响的行数
executeQuery: 执行查询操作,返回结果集
5)处理结果集(针对查操作,增删改不用)
ResultSet:处理结果集接口
6)关流操作
一般是后开先关原则,节约资源.
1,代码部分 –以查询示例
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class Demo_Select {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//1注册驱动
Class.forName("com.mysql.jdbc.Driver"); //驱动类
//2 连接准备
String url="jdbc:mysql://localhost:3306/mybase";
String user="root"; //我的数据库账号
String password="root"; //我的数据库密码
//获取连接对象
Connection con= DriverManager.getConnection(url, user, password);
//3 获取sql执行者对象
Statement state=con.createStatement();
//拼写SQL语句
String sql="select * from sort";
//4 ResultSet executeQuery(String sql) 执行SQl语句中的select
// 返回ResultSet 接口的实现类对象,也在mysql程序里
ResultSet rs= state.executeQuery(sql);
//5处理结果集
//ResultSet接口方法 boolean next() 返回true,有结果,返回false,无结果
//该光标位于第一行(数据行) 之前, 调用一次向下移动一行
while(rs.next()) {
//获取每列数据 ,使用的是ResultSet接口中的方法 getXx 该方法也有两个参数,一个是写int coumIndex,(获取的是数据库表中的第几列) 一个是String 类名 (推荐使用)
//通用方法是 getString 或者getObject 都可以接收
System.out.println(rs.getInt("sid")+" "
+ rs.getString("sname")+ " "
+rs.getDouble("sprice")+" "
+rs.getString("sdesc"));
}
//6,标准关流方式,放在finally中,确保一定能关流
try {
if (rs != null) rs.close();
if (stat != null) stat.close();
if (con != null) con.close();
} catch (SQLException e) {
}
}
}
2,自定义的JDBCUtils工具类(初级形态)
1) 解决原生JDBC存在的重复操作--封装数据库连接对象
在原生JDBC存在每个需要连接的数据库的操作,都需要重复执行获取连接对象的操作,且关闭资源流的操作也重复
这时就需要把重复操作部分封装起来,每次调用方法,直接就能获取连接对象和关流,所有就定义了一个工具类
2)存在缺点
在每个类中定义url,和user,password,写入具体的值,这样在开发中,如果需要换数据库,或者连接其他主机ip,代码固化,一旦修改就会大动干戈,修改很多,没有实现修改一处实现多处修改,实际开发中,则采用读取配置文件的方式进行改进.
步骤:a,首先在src下创建一个db.properties (数据库的配置文件)
//在src下的配置文件会在创建的时候,自动拷贝到bin目录下 ,在src下对配置文件的修改,会重新拷贝到bin下,覆盖原来的文件,
b,获得类的字节码对象,调用方法获得类加载器, 获得指定资源输入流(读取配置文件中的键值对信息)
2.1,代码部分 –封装JDBCUtils工具类
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class JdbcUtils {
//定义静态方法,返回数据库的连接对象,要把构造私有,不让别人创建对象
private static final String URL="jdbc:mysql://localhost:3306/mybase";
private static final String USER="root";
private static final String PASSWORD="root";
//将这些全部定义为静态的,私有常量,最好的方式是通过配置文件来读取这几个参数,在需要修改时,不用修改源代码
private JdbcUtils() {}; //将构造私有,不让其他类创建对象,对外只提供静态的方法,调用即可
private static Connection con=null;
static {
try {
Class.forName("com.mysql.jdbc.Driver"); //驱动类
con= DriverManager.getConnection(URL, USER, PASSWORD);
} catch (Exception e) {
throw new RuntimeException("数据库连接失败!");
}
}
//获取连接对象的方法
public static Connection getConnetion() {
return con;
}
//关闭流方法,释放资源
//在这里因为Statement接口 是PreparedStatement的父接口, 父类可传参
public static void close(Connection con,Statement stat,ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
}
}
if (stat != null) {
try {
stat.close();
} catch (SQLException e) {
}
}
if (con != null) {
try {
con.close();
} catch (SQLException e) {
}
}
}
}
2.2,代码部分 – 读取配置文件的方式
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
public class TestProperties {
public static void main(String[] args) throws IOException, ClassNotFoundException, SQLException {
//1首先获得类的字节码对象,调用方法获得类加载器(作用:加载字节码对象进内存)
ClassLoader classLoader = TestProperties.class.getClassLoader();
//2 获得指定资源输入流,资源路径相对于classPath路径,因在src下,会被自动加载到bin目录下
InputStream in=classLoader.getResourceAsStream("db.properties");
Properties pro=new Properties();
//获得集合对象,Hashtable双列集合下的子类,主要用于读取配置文件,这也是Hashtable早已过时还存在的原因,有个有用的儿子
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);
}
}
配置文件 db.properties
(以键值对方式储存,一行一对键值对,后面不要加符号),在应用需求改变,只需修改该配置文件
driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybase
username=root
password=root
3,Dbutils工具类—封装了常用的crud操作
Dbutils是JDBC的简化开发工具包,里面封装了对数据库的常用操作,使得对数据库的操作更加简单
所需jar包 commons-dbutils-1.6.jar,当然也需数据库驱动包
3大核心类:
1)QueryRunner类,提供了实现增删改查操作的API
主要方法有2个:
a,query(1,2,3,4)
用于实现查询操作,其中第1个参数,是连接对象(在与连接池结合使用时,不需要传入此参数)
第2个参数,是sql语句
第3个参数,是处理结果集的实现类对象
第4个参数,是一个可变参数 Object...objs(即对sql语句的占位符位置赋值,传入具体的参数,可变参数可以有0-n个,底层是一个数组)
b,update(1,2,3)
用于增删改操作,返回int, 第1个参数,是连接对象(在与连接池结合使用时,不需要传入此参数)
第2个参数,是sql语句
第3个参数,可变参数,也是对sql占位符的赋值
2)ResultSetHandler接口,专门用于处理结果集的
主要分8个类(其实可归纳为6种): 常用前4个
1,BeanHandler
针对查询结果只有一个时,将结果集中的第一行数据封装成一个JavaBean对象,需要传入接收的实体类字节码对象,该javaBean对象必须空参构造,因为其通过反射方式是创建空参对象
如:new BeanHandler(类.class)
2,BeanListHandler
有多个结果时,将结果集中每一行数据,封装成JavaBean对象,再放到List集合中
3,MapHandler
针对查询结果只有一个,将结果集中的第一行数据封装到Map集合中,Map<键,值> 键为表的字段名,值为对应数据
即: Map<String, Object> ,通过对Map集合中的键获取值
4,MapListHandler
针对查询结果有多个,先将每一行数据封装到Map结合中,再将Map集合存储到List集合中
即: List<Map<String,Object>>
5,ArrayHandler
针对结果一个的情况,将结果集第一行存储到Object[] 数组中
6,ArrayListHandler
同理可推 针对多个集合 List<Object[]>
7,ColumnListHandler
获取指定列的所有数据,存储到List集合中
List<Object> ColumnListHandler(列名)
8,ScalarHandler
对应查询结果只有1个结果 如 count(*) 查询个数等
3)DbUtils类,封装了各种关流和事务处理的方法
如: close(), closeQuietly()--内部已经处理过异常
* 4,连接池技术: DBCP, c3p0
1,为什么要引入连接池技术
为了避免频繁地 获取连接 和 释放资源, 会消耗系统的资源,为了节约资源,提高性能,引入了连接池的技术实现
2,DBCP连接池
1)特点:开源,tomcat内置的连接池
2)jar包:commons-dbcp.jar, commons-pool(依赖包)
3)获取步骤:dbcp.jar包中定义了一个BasicDataSource实现类去实现javax.sql.DataSource接口
a,DataSource dataSource=new BasicDataSource()//获取连接池对象
b,配置信息,连接池对象的几个方法
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/mybase");
dataSource.setUsername("root"); //数据库账号
dataSource.setPassword("root"); //数据库密码
c,获取一个连接对象
Connection con=dataSource.getConnection();
注:连接池中关流操作,只是把连接对象返还给连接池,不是真正的关闭
4)其他方法:如配置连接池的初始信息
setInitialSize(10);//连接池初始化时,创建多少个连接对象
setMaxActive(8); //设置最多可以有几个程序来获取连接对象
setMaxIdle(5); //设置最大可有几个空闲连接对象
setMinIdle(2); // 设置池中剩余最小空闲连接对象
3,c3p0连接池,
1)特点:开源,目前使用他的开源项目有hibernate,spring等
2)配置: 2个jar包1个配置文件
如: c3p0-0.9-1.2-jdk1.3.jar
c3p0-0.9-1.2.jar (请忽略版本)
配置文件: c3p0-config.xml(引入相应的约束,里面配置数据库连接的基本信息,也可以定义连接池的配置)
3)获取步骤:
1,获取连接池对象
2,通过连接池对象调用方法获取一个连接连接对象
4,两个连接池的比较:
c3p0的连接数据库配置以及连接池的初始化配置全在c3p0-config.xml中配置,通过读取配置文件的方式避免把代码写死到Java程序中,比DBCP连接池更加灵活一些,DBCP的配置都是通过连接池对象的调用方法进行配置的,所以相对要局限一些(虽然一个项目中配置一个连接池的地方不多,有需求变化,需要改动代码的地方很少,所以也可忽略)
4.1代码部分 – 通过c3p0连接池对象获取连接对象
import java.sql.Connection;
import java.sql.SQLException;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class C3p0Utils {
private static ComboPooledDataSource comboPooledDataSource;
static {
//创建一个连接池对象,指明要连接的数据库类型
comboPooledDataSource=new ComboPooledDataSource("mysql");
}
//对外只提供一个静态方法,获取连接对象
public static Connection getConneciton() {
Connection conn=null;
try {
conn=comboPooledDataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
}
4.2 c3p0-config.xml基本配置
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<!-- This is default config! -->
<default-config>
<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 is my config for mysql,这是针对mysql的配置,也可配置Oracle-->
<named-config name="mysql">
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/mybase?characterEncoding=utf-8</property>
<property name="user">root</property>
<property name="password">root</property>
<property name="initialPoolSize">20</property>
<property name="maxIdleTime">30</property>
<property name="maxPoolSize">100</property>
<property name="minPoolSize">10</property>
<property name="maxStatements">200</property>
</named-config>
</c3p0-config>
5,Dbutils结合连接池技术
注:在Dbutils工具类结合连接池后(以DBCP连接池为例),封装一个获取连接池对象的类,对于需要连接对象的类,在QueryRunner对象中传入连接池对象即可,即可获的一个连接对象,
在后续代码中执行query(),或者update()时无需传入Connction连接对象了
//QueryRunner类构造方法,接收DataSource接口的实现类
private static QueryRunner qr=
new QueryRunner(new org.apache.commons.dbcp.BasicDataSource());
//这里直接传入了DBCP的连接池对象,最好封装一个类,在类中获取连接池对象,当然要进行连接池对象数据库配置以及连接池的初始配置,如初始化连接对象(即一开始连接池对象创建多少个连接对象),最大空闲连接对象个数等等.
5.1代码部分 – 封装获取连接池对象
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.dbutils.QueryRunner;
//连接池工具类
public class JDBCUtils {
//1获取连接池对象
private static BasicDataSource dataSource=new BasicDataSource();
static {
//2 设置数据库连接必须的4个信息 ,放在静态代码块中
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/mybase");
dataSource.setUsername("root");
dataSource.setPassword("root");
//3 连接池的配置信息, 可选项
dataSource.setInitialSize(10); //连接池初始化时,创建多少个连接对象
dataSource.setMaxActive(8); //设置最多可以有几个程序来获取连接对象
dataSource.setMaxIdle(5); //设置最大可有几个空闲连接对象
dataSource.setMinIdle(2); // 设置池中剩余最小空闲连接对象
}
//3对外提供公共方法连接接口实现类, 多态,返回连接池对象
public static DataSource getDataSource() {
return dataSource;
}
}
6,JDBC以上存在的5个问题–引入Mybatis框架
1、 数据库连接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库连接池可解决此问题。
解决:在SqlMapConfig.xml中配置数据连接池,使用连接池管理数据库链接。
2、 Sql语句写在代码中造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变java代码。
解决:将Sql语句配置在XXXXmapper.xml文件中与java代码分离。
3、 向sql语句传参数麻烦,因为sql语句的where条件不一定,可能多也可能少,占位符需要和参数一一对应。
解决:Mybatis自动将java对象映射至sql语句,通过statement中的parameterType定义输入参数的类型。
4、 对结果集解析麻烦,sql变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成pojo对象解析比较方便。
解决:Mybatis自动将sql执行结果映射至java对象,通过statement中的resultType定义输出结果的类型。
7,Mybatis的简单实现步骤,优势
在Mybatis中就把各种配置都是在配置文件中来实现,避免代码写死,更具有一处修改,多处修改的功能.
在这里只是简单介绍
简单步骤:
1,首先要加载配置文件 ibatis.io包下的
方式1: Resources.getResourceAsStream(“配置文件相对路径”)
方式2: 字节码文件对象.getClassLoader().getResourceAsStream("核心配置文件")
相对于classPath的路径,其中获取配置文件的方式也可以有其他方式, 这是以字节流的方式,有配置文件方式,字符流方式等多种.
2,在第一步的原材料准备完毕后,需要创建SqlSessionFactory工厂,
其实现类 SqlSessionFactoryBulider调用方法build加载配置文件,这里是以字节流方式,
SqlSessionFactory sqlSessionFactory =new SqlSessionFactoryBuilder().bulid(inputStream);
3,在创建了SqlSessionFactory对象后,就可以创建SqlSession对象了(其与数据库交互的对象)
SqlSession sqlSession =sqlSessionFactory.openSession();
4, sqlSession 对象中有select( one, List), insert(1,2),update(1,2),delete(1,2)
即增删改查操作, 一般选用2个参数的,第一参数是参入配置文件中映射的sql语句的id名,这样才可以在配置文件中找到具体唯一的要执行的sql语句,第二个参数是传入sql语句中的占位符需要传入的参数
5,对于sqlSession对象执行完sql语句,查询不需要提交事务,其他的增改删都需要手动提交事务
6,关流操作(底层封装的是连接池,将与数据库的连接对象还给连接池)