通过单例模式创建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);
}
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;
}
}
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;
}
}
拓展:实现增删查改工具类
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);
}
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;
}
}