写了一下午,刚刚写到自己昨天学的东西。
JDBC是什么?
就是Java程序与数据库连接的一个桥梁!
首先,连接JDBC需要一些jar包,这里有连接JDBC用的全部工具
提取码:ytqx
这里默认大家会JUnit测试,会导入Jar包,不会的去学习一下,很快!
原始的JDBC
public class TestJDBC {
@Test
public void testLogin(){
try {
this.login("张三",1);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void login(String username,int uid) throws ClassNotFoundException, SQLException {
// TODO Auto-generated method stub
//加载驱动
Class.forName("com.mysql.jdbc.Driver");
//获得连接
Connection conc = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/web02","root","12345");
//sql语句
String sql = " select * from user where uid = ?";
//进行预处理
PreparedStatement ps = conc.prepareStatement(sql);
ps.setInt(1, uid);
//执行语句
ResultSet rs = ps.executeQuery();
while(rs.next()){
int id = rs.getInt(1);
String name = rs.getString(2);
System.out.println("id:"+id+" 姓名:"+name);
}
rs.close();
ps.close();
conc.close();
}
}
老生常谈一下,原始的JDBC坏处,网上都有,就是什么硬编码啊之类的。比如上边代码中,我们使用的是端口3306,web02数据库。万一那天我们换了端口呢?换了数据库名字呢?换了用户,换了密码呢?程序已经写完了,交给了用户。你总不能在对用户说,我把软件拿回来,在反编译一下,改改东西吧!因此我们需要改进!
带配置文件的JDBC
有许多我们需要改动的东西,那我们怎么办呢?我们可以通过配置文件来为其传值。
同时,在上面的代码中,我们每次书写代码都会有许多重复的部分。因此接下来我们把加载驱动、获得连接与释放资源的代码提出来。
我们把需要加载的驱动名,数据库url,用户名,密码放到配置文件中,只需修改配置文件就可以了
先看一下项目结构
我们把配置文件放到src路径下,定义了一个test包,一个utils包,lib文件夹存放jar文件。
配置文件
db.properties
使用配置文件有两种方法:
方法一:ResourceBundle加载
JDBCUtils.java(获得连接类)
public class JDBCUtils {
private static String driver;
private static String url;
private static String username;
private static String password;
//加载配置文件信息
static{
ResourceBundle rb = ResourceBundle.getBundle("db");
driver = rb.getString("driver");
url = rb.getString("url");
username = rb.getString("username");
password = rb.getString("password");
}
//获得连接
public static Connection getConection(){
Connection conc = null;
try {
//加载驱动
Class.forName(driver);
//获得连接
conc = DriverManager.getConnection(url,username,password);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return conc;
}
//释放资源
public static void release(Connection conc,PreparedStatement ps,ResultSet rs){
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(ps!=null){
try {
ps.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(conc!=null){
try {
conc.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
方法二:ClassLoader加载
public class JDBCUtil2 {
private static String driver;
private static String url;
private static String username;
private static String password;
//静态代码块导入配置文件
static{
//类加载器导入配置文件
ClassLoader classLoader = JDBCUtil2.class.getClassLoader();
InputStream resourceAsStream = classLoader.getResourceAsStream("db.properties");
//Properties类加载配置文件
Properties properties = new Properties();
try {
//导入
properties.load(resourceAsStream);
driver = properties.getProperty("driver");
url = properties.getProperty("url");
username = properties.getProperty("username");
password = properties.getProperty("password");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static Connection getConnection(){
Connection conn = null;
try {
Class.forName(driver);
conn = DriverManager.getConnection(url,username,password);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return conn;
}
}
测试
TestUtils .java (测试类)
public class TestUtils {
@Test
public void jdbcQuery() {
// TODO Auto-generated method stub
String sql = "select * from user ";
//获得连接方法调用
Connection conection = JDBCUtils.getConection();
PreparedStatement ps = null;
ResultSet rs = null;
try {
ps = conection.prepareStatement(sql);
rs = ps.executeQuery();
//循环获得返回值
while(rs.next()){
int id = rs.getInt(1);
String name = rs.getString(2);
int age = rs.getInt(3);
System.out.println("id: "+id+" name: "+name+" age "+age);
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
//释放资源
JDBCUtils.release(conection, ps, rs);
}
}
}
两种加载方法的测试其实更换一下调用的静态方法就可以了。
加上连接池技术的JDBC
上面的代码中,我们虽然带了配置文件,进行了解耦,但是连接数据库中开销最大其实是数据库的连接与释放资源。
从上边的代码还看不出来,但是如果有许多人许多人许多人来不断的连接与释放资源呢?估计服务器一会就完了。因此我们需要引入连接池技术。就是建立一个池子,每次连接用完了就放到池子里,谁用谁拿,用完返回来,这样就不用不断建立,不断释放了。
自定义连接池
首先,我们先写一个自己自定义的连接池。
实现连接池其实需要我们实现一个接口:DataSource
但是DataSource这个接口需要实现的方法太多,我们这里只实现一个getConnection方法,然后自己写一个backConnection方法.其他的方法不去实现。
只贴出实现的方法:
public class JDBCUtils implements DataSource {
private static String driver;
private static String url;
private static String username;
private static String password;
private static LinkedList<Connection> pool;
static {
//类加载
ClassLoader classLoader = JDBCUtils.class.getClassLoader();
InputStream is = classLoader.getResourceAsStream("db.properties");
//properties导入
Properties prop = new Properties();
try {
prop.load(is);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
driver = prop.getProperty("driver");
url = prop.getProperty("url");
username = prop.getProperty("username");
password = prop.getProperty("password");
pool = new LinkedList<Connection>();
//为连接池添加五个连接
try {
Class.forName(driver);
for(int i=0 ;i < 5 ;i++ ){
Connection conn = DriverManager.getConnection(url,username,password);
pool.add(conn);
}
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SQLException e){
e.printStackTrace();
}
}
@Override
public Connection getConnection(){
// TODO Auto-generated method stub
Connection conn = null;
//判断是否还有可用的连接,如若没有就创建几个
if(pool.size()==0){
for(int i = 0;i < 3;i++){
try {
conn = DriverManager.getConnection(url,username,password);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
pool.add(conn);
}
}
System.out.println("可连接总数:"+pool.size());
conn = pool.remove(0);
System.out.println("可连接总数:"+pool.size());
return conn;
}
/**
* 连接归还连接池
* */
public void backConnection(Connection conn) {
// TODO Auto-generated method stub
try {
pool.add(conn);
System.out.println("已经归还到连接池!连接池可连接数:"+pool.size());
} catch (Exception e) {
// TODO Auto-generated catch block
new RuntimeException();
}
}
.....一堆没写的没实现的方法...
}
上方的代码有个缺陷就是close()方法未被考虑到,我们应该将close()方法也改成放到连接池里,即跟上方的backConnection()方法相同。那我们要去哪里修改呢?当然是去close()方法的类里了,因此需要写一个自定义的Connection类。
而自定义Connection需要实现Connection这个接口
public class MyConnection implements Connection {
/**
* conn:需要哪个连接回到连接池
* pool:回到哪个连接池
*/
Connection conn = null;
LinkedList<Connection> pool = null;
public MyConnection(Connection conn,LinkedList<Connection> pool) {
// TODO Auto-generated constructor stub
this.conn = conn;
this.pool = pool;
}
/* (non-Javadoc)
* @see java.sql.Connection#close()
*/
@Override
public void close() throws SQLException {
// TODO Auto-generated method stub
pool.add(conn);
}
/* (non-Javadoc)
* @see java.sql.Connection#prepareStatement(java.lang.String)
*/
//这里需要更改
@Override
public PreparedStatement prepareStatement(String sql) throws SQLException {
// TODO Auto-generated method stub
return conn.prepareStatement(sql);
}
..........许多没有实现的方法.........
}
其实上边的自定义连接就是设计模式中的装饰者模式.将一个对象拿进来加工后在去执行其接下来的工作。
C3P0连接池
在开始的百度网盘链接中有要使用到的xml和要使用的jar包文件,没错,注意,要导入一个jar包,建议大家存一份。要使用直接拷贝就行了,另外注意这个xml文件的名称是固定的
c3p0-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/web03</property>
<property name="user">root</property>
<property name="password">12345</property>
<property name="initialPoolSize">5</property>
<property name="maxPoolSize">20</property>
</default-config>
<named-config name="my_c3p0">
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/web02</property>
<property name="user">root</property>
<property name="password">12345</property>
</named-config>
</c3p0-config>
上方的xml中其实是一份默认的default配置,一份自定义的my_c3p0配置,当然上边的两份配置其实就数据库名名称不一样.在下边的代码里,我们也两种配置都导入进来了,你可以使用默认的配置获得连接,也可以使用自定义的配置。
JDBCUtils3_c3p0.java
public class JDBCUtils3_c3p0 {
//使用默认配置
private static ComboPooledDataSource cp_default;
//使用自定义配置
private static ComboPooledDataSource cp_my;
//初始化配置
static{
cp_default = new ComboPooledDataSource();
cp_my = new ComboPooledDataSource("my_c3p0");
}
/**
* 获取数据源
* */
public static DataSource getDataSource(){
return cp_default;
}
/**
* 获取默认配置连接
* */
public static Connection getDefaultConnection(){
Connection conn = null;
try {
conn = cp_default.getConnection();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return conn;
}
/**
* 获取自定义配置连接
* */
public Connection getMyConnection() {
Connection conn = null;
try {
conn = cp_my.getConnection();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return conn;
}
}
DBCP连接池
相同的,上方的c3p0有配置文件,dbcp连接池也有配置文件,在开始的百度网盘链接中也有要使用到的dbcpconfig.properties文件和要使用的两个jar包,注意两个,建议大家存一份。
因为这个是properties文件所以我们要使用上面的ResourceBundle加载和ClassLoader加载配置文件这两种方式加载。
JDBCUtils4_dbcp.java
public class JDBCUtils4_dbcp {
private static DataSource ds;
static {
InputStream is = JDBCUtils4_dbcp.class.getClassLoader()
.getResourceAsStream("dbcpconfig.properties");
Properties p = new Properties();
try {
p.load(is);
ds = BasicDataSourceFactory.createDataSource(p);
} catch (Exception e) {
// TODO Auto-generated catch block
throw new RuntimeException();
}
}
/**
* 获取数据源
* */
public static DataSource getDataSource()
{
return ds;
}
/**
* 获取连接
* */
public Connection getConnection(){
try {
return ds.getConnection();
} catch (SQLException e) {
// TODO Auto-generated catch block
throw new RuntimeException();
}
}
}
下边是对于自定义连接池,c3p0,dbcp连接池的测试!
TestUtil.java 测试类
public class TestUtil {
String test_sql = "select * from user";
/**
* 测试自定义的连接池
* */
@Test
public void testQuery(){
JDBCUtils ju = new JDBCUtils();
Connection connection = ju.getConnection();
ResultSet rs = null;
try {
PreparedStatement ps = connection.prepareStatement(test_sql);
rs = ps.executeQuery();
while(rs.next()){
int id = rs.getInt(1);
String name = rs.getString(2);
int age = rs.getInt(3);
System.out.println("id:"+id+" name:"+name+" age:"+age);
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//归还连接到连接池
ju.backConnection(connection);
}
/**
* 测试加强了close()的自定义连接池
* */
@Test
public void testQuery1() {
//获得连接
JDBCUtils ju = new JDBCUtils();
Connection conn = ju.getConnection();
PreparedStatement ps = null;
ResultSet rs = null;
try {
ps = conn.prepareStatement(test_sql);
rs = ps.executeQuery();
while(rs.next()){
int id = rs.getInt(1);
String name = rs.getString(2);
int age = rs.getInt(3);
System.out.println("id:"+id+" name:"+name+" age:"+age);
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
try {
rs.close();
ps.close();
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/**
* 测试c3p0连接池
* */
@Test
public void testQuery2(){
JDBCUtils3_c3p0 juc = new JDBCUtils3_c3p0();
Connection conn = juc.getDefaultConnection();
try {
PreparedStatement ps = conn.prepareStatement(test_sql);
ResultSet rs = ps.executeQuery();
while(rs.next()){
int id = rs.getInt(1);
String name = rs.getString(2);
int age = rs.getInt(3);
System.out.println("id:"+id+" name:"+name+" age:"+age);
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 测试dbcp连接池
* */
@Test
public void testQuery3() {
JDBCUtils4_dbcp jud = new JDBCUtils4_dbcp();
//获得连接
Connection conn = jud.getConnection();
try {
PreparedStatement ps = conn.prepareStatement(test_sql);
ResultSet rs = ps.executeQuery();
while(rs.next()){
int id = rs.getInt(1);
String name = rs.getString(2);
int age = rs.getInt(3);
System.out.println("id:"+id+" name:"+name+" age:"+age);
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
终于到最后一个了,写的有点恶心了,写了一下午的博客,明明今晚上要玩会LOL的。结果都19:50了
DBUtil的使用
为啥要使用?还不是,程序员觉得敲得代码太多,想偷懒!通过这个,我们可以进一步减少那些CRUD的代码,就上边测试类中的那些代码。
我是受不了了!我要放假!我贴代码了!
一个要诀!:记住那个核心类QueryRunner
public class TestDBUtils {
/**
* 使用DBUtil添加
* */
@Test
public void testUpdate(){
JDBCUtils3_c3p0 juc = new JDBCUtils3_c3p0();
//配置dbutil核心类
QueryRunner qr = new QueryRunner(juc.getDataSource());
//sql语句
String sql = "insert into user value (?,?,?)";
//参数数组
Object[] param = {"smoke","7"};
//运行命令
try {
int i = qr.update(sql, param);
if(i>0){
System.out.println("更新成功");
}else{
System.out.println("失败");
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* dbutil单个查询
* */
@Test
public void testQuery1() {
//配置核心QueryRunner
QueryRunner qr = new QueryRunner(JDBCUtils3_c3p0.getDataSource());
//SQL语句
String sql = "select * from user where uid=?";
Object[] param = {"1"};
try {
User user = qr.query(sql,param, new BeanHandler<User>(User.class));
System.out.println(user);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* dbutil全部查询
* */
@Test
public void testQuery2() {
//配置核心QueryRunner
QueryRunner qr = new QueryRunner(JDBCUtils3_c3p0.getDataSource());
//SQL语句
String sql = "select * from user";
try {
List<User> user = qr.query(sql, new BeanListHandler<User>(User.class));
for(User u : user){
System.out.println(u);
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}