MySQL JDBC 及其连接池

JDBC

JDBC,即Java数据库连接,是SUN公司推出的Java访问数据库的标准规范(接口)。
1. JDBC是一种用于执行SQL语句的Java API。
2. JDBC可以为多种关系数据库提供统一访问入口。
3. JDBC是由一组Java工具类和接口组成。


JDBC开发步骤
1. 注册驱动。
2. 获得连接。
3. 获得语句执行者。
4. 执行sql语句。
5. 处理结果。
6. 释放资源。


1、导入jar包

a、创建lib目录,用于存放当前项目需要的所有jar包
b、选择jar包,右键执行 bulid path / add to bulid Path

2、注册驱动

Class.forName("com.mysql.jdbc.Driver");

3、获取连接

Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/demo","root","wu25471396");

扩展url参数

jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=UTF8
//注意:java程序连接数据库的过程中,使用的字符编码是UTF-8。mysql中指定UTF-8编码是UTF8.

4、获得语句执行

方法一:推荐(可以防止SQL注入)

String sql = "select * from tb1_user where uname=? and upassword=? ";
//创建预处理对象
PrepareStatement pstmt = conn.prepareStatement(sql);
//设置参数(给占位符)
pstmt.setString(1,username);
pstmt.setString(2,password);

方法二:

String sql = "select * from tb1_user where uname="+username+" and upassword="+password";
//创建执行sql语句的对象
Statement stmt = conn.createStatement();

4、处理结果集

ResultSet rs = pstmt.executeQuery();
或(与上面的相对应的)
ResultSet rs = stmt.executeQuery(sql);

5、释放资源

rs.close();
stmt.close();
con.close();

JDBC入门

入门案例:


public void login1(String username, String password) throws ClassNotFoundException, SQLException {
        // 1.注册驱动
        Class.forName("com.mysql.jdbc.Driver");
        // 2.获取连接
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/web08", "root", "root");
        // 3.编写sql语句
        String sql = "select * from user where uname=? and upassword=?";
        // 4.创建预处理对象
        PreparedStatement pstmt = conn.prepareStatement(sql);
        // 5.设置参数(给占位符)
        pstmt.setString(1, username);
        pstmt.setString(2, password);
        // 6.执行查询操作
        ResultSet rs = pstmt.executeQuery();
        // 7.对结果集进行处理
        if (rs.next()) {
            System.out.println("恭喜您," + username + ",登录成功!");
            System.out.println(sql);
        } else {
            System.out.println("账号或密码错误!");
        }
        if (rs != null)
            rs.close();
        if (pstmt != null)
            pstmt.close();
        if (conn != null)
            conn.close();
    }

    public void login(String username, String password) throws ClassNotFoundException, SQLException {
        // 1.注册驱动
        Class.forName("com.mysql.jdbc.Driver");
        // 2.获取连接
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/web08", "root", "root");
        // 3.创建执行sql语句的对象
        Statement stmt = conn.createStatement();
        // 4.书写一个sql语句
        String sql = "select * from user where uname='" + username + "' and upassword='" + password + "'";
        // 5.执行sql语句
        ResultSet rs = stmt.executeQuery(sql);
        // 6.对结果集进行处理
        if (rs.next()) {
            System.out.println("恭喜您," + username + ",登录成功!");
            System.out.println(sql);
        } else {
            System.out.println("账号或密码错误!");
        }
        if (rs != null)
            rs.close();
        if (stmt != null)
            stmt.close();
        if (conn != null)
            conn.close();
    }

编写JDBCUtils工具类

使用properties配置文件:
1. 文件位置:任意,建议src下
2. 文件名称:任意,扩展名为properties
3. 文件内容:一行一组数据。格式是key=value (key命名自定义,如果多个单词,习惯用点分隔。)

假如文件为:db.properties
内容:

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/demo
username=root
password=wu25471396

JDBCUtils_v1版本:
使用JDK提供的工具类ResourceBundle加载properties文件

public class JDBCUtils_v1 {

    private static String driver;   //驱动
    private static String url;      //路径
    private static String user;     //用户名
    private static String password; //密码
    /**
     * 配置文件只需要加载一次,提供静态代码,当前类被加载到内存执行
     */
    static{
        //1、使用JDK提供的工具类加载properties文件
        /*
         *  ResourceBundle专门用于处理properties文件的。
         *  提供getBundle()方法只需要填写文件即可。
         */
        ResourceBundle bundle = ResourceBundle.getBundle("db");
        //2、通过key获取需要的值
        driver = bundle.getString("driver");
        url = bundle.getString("url");
        user = bundle.getString("username");
        password = bundle.getString("password");
    }
    /**
     * 获取连接
     * @return
     */
    public static Connection getConnection(){
        try {
            //1、注册驱动
            Class.forName(driver);
            //2、获取连接
            Connection conn = DriverManager.getConnection(url, user, password);
            return conn;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }   
    }
    /**
     * 释放资源
     * @param conn
     * @param pstmt
     * @param rs
     */
    public static void close(Connection conn, PreparedStatement pstmt, ResultSet rs){
        if (rs != null) {
            try {
                rs.close();
            }catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(pstmt != null ) {
            try {
                pstmt.close();
            }catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

JDBCUtils_v2版本:
采用加载properties文件获得流,然后使用Properties对象进行处理

public class JDBCUtils_v2 {

    private static String driver;   //驱动
    private static String url;      //路径
    private static String user;     //用户名
    private static String password; //密码
    /**
     * 静态代码块加载配置文件信息
     */
    static{
        try {
        // 1.通过当前类获取类加载器
        ClassLoader classLoader = JDBCUtils_v2.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");
        user = props.getProperty("username");
        password = props.getProperty("password");
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
    /**
     * 获取连接
     * @return
     */
    public static Connection getConnection(){
        Connection conn = null;
        try {
            //1、注册驱动
            Class.forName(driver);
            //2、获取连接
            conn = DriverManager.getConnection(url, user, password);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }   
        return conn;
    }
    /**
     * 释放资源
     * @param conn
     * @param pstmt
     * @param rs
     */
    public static void close(Connection conn, PreparedStatement pstmt, ResultSet rs){
        if (rs != null) {
            try {
                rs.close();
            }catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(pstmt != null ) {
            try {
                pstmt.close();
            }catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

测试JDBCUtils工具

public class TestUtils {
    @Test
    public void testFindUserById() {
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            // 1.获取连接
            conn = JDBCUtils_v1.getConnection();
            // 2.编写sql语句
            String sql = "select * from user where uid=?";
            // 3.获取执行sql语句对象
            pstmt = conn.prepareStatement(sql);
            // 4.设置参数
            pstmt.setInt(1, 2);
            // 5.执行查询操作
            rs = pstmt.executeQuery();
            // 6.处理结果集
            while (rs.next()) {
                System.out.println(rs.getString(2) + "---------" + rs.getString("upassword"));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 7.释放资源
            JDBCUtils_v1.close(conn, pstmt, rs);
        }
    }
}

JDBC 连接池与DBUtils

连接池概述:

  • 因为“获得连接”和“释放连接”非常消耗资源,因此在开发中,实验连接池,来共享Connection。
  • Java为数据库连接池提供了公共的接口:javax.sql.DataSource,各厂商需要让自己的连接池实现这个接口。
  • 常见测连接池DBCP、C3P0(占了开发使用的80%)。

自定义连接池:
MyDataSource.java

public class MyDataSource implements DataSource {
    //1.创建1个容器用于存储Connection对象 ,增删操作多用LinkedList
    private static LinkedList<Connection> pool = new LinkedList<Connection>();

    //2.创建5个连接放到容器中去
    static{
        for (int i = 0; i < 5; i++) {
            Connection conn = JDBCUtils_v2.getConnection();
            pool.add(conn);
        }
    }

    /**
     * 重写获取连接的方法
     */
    @Override
    public Connection getConnection() throws SQLException {
        Connection conn = null;
        //3.使用前先判断
        if(pool.size()==0){
            //4.池子里面没有,我们再创建一些
            for (int i = 0; i < 5; i++){
                conn = JDBCUtils_v2.getConnection();
                pool.add(conn);
            }
        }
        //5.从池子里面获取一个连接对象Connection
        conn = pool.remove(0);
        return conn;
    }

    /**
     * 归还连接对象到连接池中
     */
    public void backConnection(Connection conn){
        pool.add(conn);
    }
    @Override
    public PrintWriter getLogWriter() throws SQLException {
        return null;
    }

    @Override
    public void setLogWriter(PrintWriter out) throws SQLException {
    }

    @Override
    public void setLoginTimeout(int seconds) throws SQLException {
    }

    @Override
    public int getLoginTimeout() throws SQLException {
        return 0;
    }

    @Override
    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
        return null;
    }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        return null;
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return false;
    }


    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        return null;
    }

}

TestMyDataSource.java

public class TestMyDataSource {
    /**
     * 添加用户
     * 使用未改造过的connection
     */
    @Test
    public void testAddUser(){
        Connection conn = null;
        PreparedStatement pstmt = null;
        //1. 创建自定义连接池对象
        MyDataSource dataSource = new MyDataSource();
        try {
            // 2.从池子中获取连接
            conn = dataSource.getConnection();
            String sql = "insert into user values(null,?,?)";
            pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, "貂蝉");
            pstmt.setString(2, "吕布");
            int rows = pstmt.executeUpdate();
            if (rows > 0) {
                System.out.println("添加成功");
            } else {
                System.out.println("添加失败");
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            dataSource.backConnection(conn);
        }
    }
}

自定义连接池:方法增强
上述存在的问题:使用自定义连接池,则需要记住自定义连接池中的API。
例如:
当返还资源是,我们希望仍然调用close()方法。此时就需要增添close()方法。


方法增强的三种模式:
1. 继承:子类继承父类,将父类的方法进行重写,从而增强。
使用前提: 必须有父类,且存在继承关系。
2. 装饰者模式:此设计模式专门用于增强方法。
使用前提: 必须有接口。
缺点: 需要实现接口的所有方法。
3. 动态代理: 在运行时动态创建代理类,完成增强操作。与装饰者相似。
使用前提: 必须有接口。
难点: 需要反射技术。


采用装饰者模式:
装饰类:MyConnection.java

/**
 * 装饰类MyConnection 实现同一个接口Connection
 * @author ludada
 *
 */
public class MyConnection implements Connection{
    //定义一个变量
    private Connection conn;
    private LinkedList<Connection> pool;

    //编写一个构造方法(参数使用了面相对象的多态特性)
    public MyConnection(Connection conn,LinkedList<Connection> pool) {
        this.conn = conn;
        this.pool = pool;
    }
    //书写需要增强的方法
    @Override
    public void close() throws SQLException {
        pool.add(conn);
    }
    /**
     * 此方法必须覆盖,否则会出现空指针异常
     */
    @Override
    public PreparedStatement prepareStatement(String sql) throws SQLException {
        return conn.prepareStatement(sql);
    }
}

MyDataSource1.java

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 = JDBCUtils_v2.getConnection();
            //放入池子中connection对象已经经过改造了
            MyConnection myconn = new MyConnection(conn, pool);
            pool.add(myconn);
        }
    }
    /**
     * 重写获取连接的方法
     */
    @Override
    public Connection getConnection() throws SQLException {
        Connection conn = null;
        if(pool.size()==0) {
            for (int i = 0; i < 5; i++) {
                conn = JDBCUtils_v2.getConnection();
                //放入池子中connection对象已经经过改造了
                MyConnection myconn = new MyConnection(conn, pool);
                pool.add(myconn);

            }
        }
        conn = pool.remove(0);
        return conn;
    }
}

测试类:

    @Test
    public void testAddUser1() {
        Connection conn = null;
        PreparedStatement pstmt = null;
        // 1.创建自定义连接池对象
        DataSource dataSource = new MyDataSource1();
        try {
            conn = dataSource.getConnection();
            String sql = "insert into user values(null,?,?)";
            pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, "牡丹");
            pstmt.setString(2, "仙子");
            int rows = pstmt.executeUpdate();
            if(rows > 0) {
                System.out.println("添加成功");
            }else {
                System.out.println("添加失败");
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }finally{
            //注意:此处的conn是利用的多态,JDBCUtils_v2.close()中调用的conn.close()是MyConnection中close的重写方法。
            JDBCUtils_v2.close(conn, pstmt, null);;
        }
    }

常见的开源数据库连接池

C3P0

1、C3P0


C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate,Spring等。


2、C3P0的使用


  1. 导入jar包(在此我们使用0.9.2版本)。
    C3p0-0.9.2-pre5.jarmchange-commons-java-0.2.3.jar(可以没有)
  2. 配置文件
    配置文件名称:c3p0-config.xml (固定)
    配置文件位置:src (类路径)
    配置文件内容:
    以下给出一份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:///demo</property>
        <property name="user">root</property>
        <property name="password">wu25471396</property>
        <property name="initialPoolSize">5</property>
        <property name="maxPoolSize">20</property>
    </default-config>

    <named-config name="xiaobai">
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql:///demo</property>
        <property name="user">root</property>
        <property name="password">wu25471396</property>
    </named-config>
</c3p0-config>
  1. 利用C3P0编写工具类
/**
 * C3P0提供的核心工具类:ComboPooledDataSource,
 * 如果要使用连接池,必须
 * @author ludada
 *
 */
public class C3P0Utils {
    /*
     *  C3P0提供的核心工具类:ComboPooledDataSource,
     *  如果要使用连接池,必须创建该实例对象
     */
    //使用配置文件"默认配置"
    //private static ComboPooledDataSource dataSource = new ComboPooledDataSource();

    //使用配置文件"命名配置"
    private static ComboPooledDataSource dataSource = new ComboPooledDataSource("xiaobai");
    /**
     * 获得数据源(连接池)
     * @return
     */
    public static DataSource getDataSource(){
        return dataSource;
    }
    public static Connection getConnection(){
        try {
            return dataSource.getConnection();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}
  1. 测试:
public class TestC3P0 {
    @Test
    public void testAddUser() {
        Connection conn = null;
        PreparedStatement pstmt = null;
        try {
            conn = C3P0Utils.getConnection();
            String sql = "insert into user values(null,?,?)";
            pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, "靖公主");
            pstmt.setString(2, "严霜");
            int rows = pstmt.executeUpdate();
            if (rows > 0){
                System.out.println("添加成功");
            }else {
                System.out.println("添加失败");
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally{
            JDBCUtils_v2.close(conn, pstmt, null);
        }

    }
}

DBCP

1、DBCP也是一个开源的连接池,是tomcat内置的连接池。


2、DBCP的使用:
1. 导入jar包
commons-dbcp-1.4.jarcommons-pool-1.6.jar
2. 配置文件
配置文件名称:*.properties 注意: 文件中用户的key必须为username
配置文件位置:任意,建议src (classpath/类路径)
配置文件内容:不能中文
3. 编写工具类

public class DBCPUtils {
    private static DataSource dataSource;
    static{
        try {
            //1.加载找properties文件输入流
            InputStream is = DBCPUtils.class.getClassLoader().getResourceAsStream("db.properties");
            //2.加载输入流
            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;
    }

    public static Connection getConnection(){
        try {
            return dataSource.getConnection();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}
  1. 测试
public class TestDBCP {
    @Test
    public void testUpdateUserById(){
        Connection conn = null;
        PreparedStatement pstmt = null;
        try {
            conn = DBCPUtils.getConnection();
            String sql = "update user set upassword=? where uid=?";
            pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, "严霜");
            pstmt.setInt(2, 7);
            int rows = pstmt.executeUpdate();
            System.out.println(rows);
            if(rows > 0) {
                System.out.println("修改成功");
            }else{
                System.out.println("修改失败");
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            JDBCUtils_v2.close(conn, pstmt, null);
        }
    }

使用DBUtils 增删改查

**DBUtils:**apache commons组件的一个成员,封装了对JDBC的操作,简化了JDBC操作。

JavaBean组件


JavaBean 就是一个类,在开发中常用与封装数据。需要以下要求:
1. 需要实现接口:java.io.Serializable,常常偷懒省略了。
2. 提供私有字段: private 类型 字段名;
3. 提供 get/set 方法;
4. 提供无参构造方法

例:

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核心功能介绍

  • QureyRunner 中提供了对sql语句操作的API。
  • ResultSetHandler接口,用于定义select后,怎样封装结果集。
  • DBUtils类,定义了关闭资源与事务处理的方法

1、QueryRunner核心类
- QueryRunner(DataSource ds),提供数据源(连接池),DBUtils底层子对维护connection
- update(String sql, Object…params),执行更新数据。
- query(String sql,ResultSetHandlerrsh,Object…params),执行查询


2、ResultSetHandler结果处理类
我们知道在执行select语句之后得到的是ResultSet,然后我们还需要对ResultSet进行转换,得到最终我们想要的数据。你可以希望把ResultSet的数据放到一个List中,也可能想把数据放到一个Map中,或是一个Bean中。
DBUtils提供了一个接口ResultSetHandler,它就是用来ResultSe它就是用来ResultSet转换成目标类型的工具t转换成目标类型的工具。你可以自己去实现这个接口,把ResultSet转换成你想要的类型。
DBUtils提供了很多个ResultSetHandler接口的实现,这些实现已经基本够用了,我们通常不用自己去实现ResultSet接口了。

ResultSetHandler接口的实现 解释
BeanHandler 单行处理器!把结果集转换成Bean,该处理器需要Class参数,即Bean的类型
BeanListHandler 多行处理器!把结果集转换成List
ScalarHandler 单行单列处理器!把结果集转换成Object。一般用于聚集查询,例如select count(*) from tab_student。
MapHandler 单行处理器!把结果集转换成Map

猜你喜欢

转载自blog.csdn.net/longge0508/article/details/80177013
今日推荐