JDBC、Druid 单例模式加载和CRUD实现

通过单例模式创建JDBC,以及使用Durid和QueryRunner实现增删查改

优点:单例模式保证java应用程序中,一个类Class只有一个实例在,使用单例模式好处在于可以节省内存,节约资源,对于一般频繁创建和销毁对象的可以使用单例模式。 因为它限制了实例的个数,有利于java垃圾回收。好的单例模式也能提高性能。例如:数据库连接池、httpclient连接单例 对于系统中的某些类来说,只有一个实例很重要,Windows中就只能打开一个任务管理器,如何保证一个类只有一个实例并且这个实例易于被访问呢?定义一个全局变量可以确保对象随时都可以被访问,但不能防止我们实例化多个对象。一个更好的解决办法是让类自身负责保存它的唯一实例。这个类可以保证没有其他实例被创建,并且它可以提供一个访问该实例的方法。这就是单例模式的模式动机。 Singleton通过将构造方法限定为private避免了类在外部被实例化,在同一个虚拟机范围内,Singleton的唯一实例只能通过getInstance()方法访问。

缺点:有的为了节省资源创建单例模式,可能会导致共享连接池的对象程序过多,出现连接池溢出等问题。 1.单例的缺点 就是不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态。 2.单例模式的构造函数是静态的,所以在继承单例时,出现问题,不能被子类继承
单例模式优缺点参考

1.创建db.properties配置文件

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test?useSSL=true&serverTimezone=UTC&characterEncoding=UTF-8
user=root
pass=root

2.创建Env单例模式类工具

package util;

import java.io.IOException;
import java.util.Properties;

public class Env extends Properties {
//全局的Env实例对象
    private static Env instance = null;
//、单例模式,构造器必须私有化,目的是不能随意在外界通过构造器创建对象
    private Env() {
        try {
            load(Env.class.getResourceAsStream("/db.properties"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
//单例模式必须提供公有的,静态的,返回值为当前实例的一个方法,当前方法目的是在该类的外界可以拿到Env实例对
    public static Env getInstance() {
        if (instance == null) {
            instance = new Env();
        }
        return instance;
    }
}

3.测试Env类方法

package test;

import com.example.util.Env;

/**
 * Created by .
 */
public class TestEvn {

    public static void main(String[] args) {
        System.out.println(Env.getInstance().getProperty("url"));
        //jdbc:mysql://localhost:3306/test?useSSL=true&serverTimezone=UTC&characterEncoding=UTF-8
    }
}

4.创建DBUtils工具类连接数据库

(注:记得导入所需要的jar包 mysql-connector-java-数据库版本.jar commons-dbutils-*.jar)

package com.example.util;

import java.sql.*;

/**
 * Created by .
 */
public class DBUtil {

    private static Connection conn = null;
//    private static final String DB_DRIVER = "com.mysql.jdbc.Driver";
//    private static final String DB_URL = "jdbc:mysql://localhost:3306/test?serverTimezone=UTC&characterEncoding=UTF-8";
//    private static final String DB_USER = "root";
//    private static final String DB_PASS = "root";
 //通过单例模式加载
    private static final String DB_DRIVER = Env.getInstance().getProperty("driver");
    private static final String DB_URL = Env.getInstance().getProperty("url");
    private static final String DB_USER = Env.getInstance().getProperty("user");
    private static final String DB_PASS = Env.getInstance().getProperty("pass");

    /**
     * 静态代码块,目的是为了让驱动在第一时间被加载
     */
    static {
        try {
            Class.forName(DB_DRIVER);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取数据库连接对象
     * @return 数据库连接对象
     */
    public static Connection getConnection(){
        try {
            conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASS);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }

        return conn;
    }
    /**
     * 关闭所有的数据库连接资源
     * @param rs 要关闭的结果集对象
     * @param stmt 要关闭的声明对象
     * @param conn 要关闭的连接对象
     */
    public  static void closeAll(ResultSet rs, Statement stmt, Connection conn){
        try {
            if(rs != null){
                rs.close();
            }
            if(stmt != null){
                stmt.close();
            }
            if(conn != null){
                conn.close();
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
}

5.这里写一个登陆案例

  • 创建接口
public interface IStudentDao {
    Student login(String name, String pass);
}

  • 接口实现类(存在sql注入)
package com.example.dao.impl;

import com.qfedu.dao.IStudentDao;
import com.qfedu.entity.Student;
import com.qfedu.util.DBUtil;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

/**
 * Created by .
 */
public class StudentDaoImpl implements IStudentDao {

    private Connection conn = null;
    private Statement stmt = null;
    private ResultSet rs = null;

    @Override
    public Student login(String name, String pass) {

        Student stu = null;

        conn = DBUtil.getConnection();

        String sql = String.format("select * from student where sname = '%s' and pass = %s", name, pass);

        try {
            stmt = conn.createStatement();

            rs = stmt.executeQuery(sql);

            while(rs.next()){
                stu = new Student();

                stu.setSid(rs.getInt(1));
                stu.setSname(rs.getString(2));
                stu.setPass(rs.getString(3));
                stu.setScore(rs.getDouble(4));
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            DBUtil.closeAll(rs, stmt, conn);
        }

        return stu;
    }
}

  • 防止sql注入的方法类
package com.example.dao.impl;

import com.qfedu.dao.IStudentDao;
import com.qfedu.entity.Student;
import com.qfedu.util.DBUtil;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * Created by .
 */
public class StudentDaoImpl02 implements IStudentDao {
    private Connection conn = null;
    private PreparedStatement ptst = null;
    private ResultSet rs = null;

    @Override
    public Student login(String name, String pass) {

        Student stu = null;

        conn = DBUtil.getConnection();

        String sql = String.format("select * from student where sname = ? and pass = ?");

        try {
            ptst = conn.prepareStatement(sql);

            ptst.setString(1, name);
            ptst.setString(2, pass);

            rs = ptst.executeQuery();

            while (rs.next()){
                stu = new Student();

                stu.setSid(rs.getInt(1));
                stu.setSname(rs.getString(2));
                stu.setPass(rs.getString(3));
                stu.setScore(rs.getDouble(4));
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            DBUtil.closeAll(rs, ptst, conn);
        }

        return stu;
    }
}

拓展:实现增删查改工具类

  • 创建RowMapper接口工具
package com.example.util;

import java.sql.ResultSet;

/**
 * Created by .
 * 工具接口,将resultset对象转换成泛型T对象
 */
public interface RowMapper<T> {

    /**
     * 工具方法,将resultset转换为t对象
     * @param rs 结果集对象
     * @return 该结果集对应的对象类型
     */
    T getRow(ResultSet rs);
}

  • 创建Daoutil工具类
package com.example.util;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by .
 * dao的工具方法,能够将所有sql语句进行执行
 *
 *  commonsUpdate(sql, params)实现所有的表的增删改任务,返回值为受影响的行数int的值
 *  commonsQuery(sql, rowmapper, params)实现所有表的查询,返回值为对应对象的列表
 */
public class DaoUtil<T> {

    private static Connection conn = null;
    private static PreparedStatement ptst = null;
    private static ResultSet rs = null;

    /**
     * 通用的增删改的工具方法
     * @param sql 含有问号的sql语句
     * @param params 要对sql语句中的问号进行值的填充,参数个数类型与顺序都要与sql语句中的问号占位符保持一致
     * @return 执行该sql语句后受影响的行数
     */
    public static int commonsUpdate(String sql, Object ... params){
        int result = 0;

        //  获取连接对象
        conn = DBUtil.getConnection();

        try {
            //  通过连接对象的prepareStatement(sql)将sql语句作为参数得到一个PreparedStatment对象
            ptst = conn.prepareStatement(sql);

            //  循环给sql语句中的问号占位符填充对应的值
            for (int i = 0; i < params.length; i++) {
                Object p = params[i];
                ptst.setObject(i + 1, p);
            }

            //  使用PreparedStatement对象的executeUpdate()完成数据库增删改功能,得到受影响的行数
            result = ptst.executeUpdate();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            DBUtil.closeAll(rs, ptst, conn);
        }

        return result;
    }
    /**
     *
     * 该方法完成sql语句的查询,并且将结果集转换成对应的对象的list集合
     * @param sql 要查询的sql语句
     * @param rowMapper 工具接口,能够实现将结果集转换为list对象
     * @param params sql语句的问号参数的占位符的值,要注意个数、顺序和类型
     * @return 对应对象的列表list
     */
    public List<T> commonsQuery(String sql, RowMapper<T> rowMapper,  Object ... params){
        List<T> list = null;

        conn = DBUtil.getConnection();

        try {
            ptst = conn.prepareStatement(sql);

            for (int i = 0; i < params.length; i++) {
                Object p = params[i];
                ptst.setObject(i + 1, p);
            }

            rs = ptst.executeQuery();


            if(rs != null){
                list = new ArrayList<>();
                while (rs.next()){
                    //  遍历结果集对象,将每个结果集对接使用RowMapper工具接口,将resultSet转换成t对象
                    T t = rowMapper.getRow(rs);

                    //  再将t对象加入到list集合中
                    list.add(t);
                }
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            DBUtil.closeAll(rs, ptst, conn);
        }

        return list;
    }
}

案例:
创建接口:
package com.example.dao;

import com.qfedu.entity.Group;

import java.util.List;

/**
 * Created by .
 */
public interface IGroupDao {

    /**
     * 根据gid查询该gid对应给Group对象
     * @param gid 要查询的group的id
     * @return 该gid对应的group对象
     */
    Group getGroupByGid(int gid);

    /**
     * 新增group对象到数据库中
     * @param g 要新增的group对象
     * @return 新增对象的返回受影响的行数
     */
    int saveGroup(Group g);

    /**
     * 分页查询
     * @param cp 当前第几页
     * @param ps 每页最大条目数
     * @return 对象的集合
     */
    List<Group> getGroupsByPage(int cp, int ps);
}

创建实现RowMapper接口类
package com.example.dao.impl;

import com.qfedu.entity.Group;
import com.qfedu.util.RowMapper;

import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * Created by .
 *
 * 使用工具结构,将Group充当泛型T
 *  所有直接T出现的地方都有Group来担当
 */
public class GroupMapper implements RowMapper<Group> {

    /**
     * 遍历结果集对象Resultset,并将resultset中的所有数据使用Group对象来接收
     * @param rs 结果集对象
     * @return group 对象
     */
    @Override
    public Group getRow(ResultSet rs) {

        Group g = new Group();

        try {
            g.setGid(rs.getInt(1));
            g.setGname(rs.getString(2));
            g.setGdesc(rs.getString(3));
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }

        return g;
    }
}

调用DaoUtil工具类实现 增、查、改
package com.example.dao.impl;

import com.qfedu.dao.IGroupDao;
import com.qfedu.entity.Group;
import com.qfedu.util.DaoUtil;

import java.util.List;

/**
 * Created by .
 
 */
public class GroupDaoImpl implements IGroupDao {
    @Override
    public Group getGroupByGid(int gid) {
        DaoUtil<Group> du = new DaoUtil<>();

        List<Group> list = du.commonsQuery("select * from tb_group where gid = ?", new GroupMapper(), gid);

        return list.get(0);
    }

    @Override
    public int saveGroup(Group g) {
        int result = DaoUtil.commonsUpdate("insert into tb_group values(?, ?, ?)",
                g.getGid(), g.getGname(), g.getGdesc());
        return result;
    }

    @Override
    public List<Group> getGroupsByPage(int cp, int ps) {
        DaoUtil<Group> du = new DaoUtil<>();

        int si = (cp - 1) * ps;

        List<Group> list = du.commonsQuery("select * from tb_group limit ?, ?", new GroupMapper(), si, ps);

        return list;
    }

    public int updateGroup(Group g){
        int result = DaoUtil.commonsUpdate("update tb_group set gname = ?, gdesc = ? where gid = ?",
                g.getGname(), g.getGdesc(), g.getGid());
        return result;
    }
}

(可以自己测试一下)

单例模式实现Druid工具类

注:导入druid所需要的包

package com.example.util;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.IOException;
import java.util.Properties;

/**
 * Created by .
 */
public class DruidDatasourceDBUtil {

    private static DruidDataSource dataSource = null;


    static {

        Properties p = new Properties();
        try {
            p.load(DruidDatasourceDBUtil.class.getResourceAsStream("/db.properties"));
            /*这里的createDataSource方法中存在默认的连接数据库的配置名为 url、driver、username、password,与创建的properties有所不同,可以通过单例模式加载*/
            dataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(p);
            
            
            dataSource.setUrl(Env.getInstance().getProperty("url"));
            dataSource.setDriverClassName(Env.getInstance().getProperty("driver"));
            dataSource.setUsername(Env.getInstance().getProperty("user"));
            dataSource.setPassword(Env.getInstance().getProperty("pass"));
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取数据库数据源对象
     * @return 数据源对象
     */
    public static DataSource getDataSource(){
        return dataSource;
    }
}

可以使用QueryRunner类中方法实现CRUD
例:

package com.example.dao.impl;

import com.qfedu.dao.IPersonDao;
import com.qfedu.entity.Person;
import com.qfedu.util.DruidDatasourceDBUtil;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanListHandler;

import java.sql.SQLException;
import java.util.List;

/**
 * Created by .
 */
public class PersonDaoImpl implements IPersonDao {

    private QueryRunner qr = new QueryRunner(DruidDatasourceDBUtil.getDataSource());

    @Override
    public List<Person> getAllPersons() {
        try {
            return qr.query("select * from person", new BeanListHandler<Person>(Person.class));
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return null;
    }

    @Override
    public int savePerson(Person p) {
        try {
            return qr.update("insert into person values(?, ?, ?, ?, ?, ?)",
                    p.getPid(), p.getPname(), p.getPpass(), p.getAge(), p.getTel(), p.getAddr());
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return 0;
    }
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值