Car*_*ara 23 java dao connection-pooling jdbc genericdao
我的问题如下.我需要一个类作为Web系统中数据库连接的单点,因此避免让一个用户有两个打开的连接.我需要尽可能优化它,它应该管理系统中的每个事务.换句话说,只有该类应该能够实例化DAO.为了使它更好,它还应该使用连接池!我该怎么办?
Car*_*ara 84
您需要实现DAO Manager.我从这个网站上获取了主要想法,但是我做了自己的实现,解决了一些问题.
首先,您必须配置连接池.连接池就是一个连接池.当您的应用程序运行时,连接池将启动一定数量的连接,这样做是为了避免在运行时创建连接,因为这是一项昂贵的操作.本指南并不是要解释如何配置一个,所以去看看.
为了记录,我将使用Java作为我的语言,使用Glassfish作为我的服务器.
让我们从创建一个DAOManager类开始.让我们给它在运行时打开和关闭连接的方法.没什么太花哨的.
public class DAOManager {
public DAOManager() throws Exception {
try
{
InitialContext ctx = new InitialContext();
this.src = (DataSource)ctx.lookup("jndi/MYSQL"); //The string should be the same name you're giving to your JNDI in Glassfish.
}
catch(Exception e) { throw e; }
}
public void open() throws SQLException {
try
{
if(this.con==null || !this.con.isOpen())
this.con = src.getConnection();
}
catch(SQLException e) { throw e; }
}
public void close() throws SQLException {
try
{
if(this.con!=null && this.con.isOpen())
this.con.close();
}
catch(SQLException e) { throw e; }
}
//Private
private DataSource src;
private Connection con;
}
Run Code Online (Sandbox Code Playgroud)
这不是一个非常花哨的课程,但它将成为我们要做的事情的基础.所以,这样做:
DAOManager mngr = new DAOManager();
mngr.open();
mngr.close();
Run Code Online (Sandbox Code Playgroud)
应该打开和关闭与对象中数据库的连接.
现在,如果我们这样做了什么?
DAOManager mngr1 = new DAOManager();
DAOManager mngr2 = new DAOManager();
mngr1.open();
mngr2.open();
Run Code Online (Sandbox Code Playgroud)
有人可能会争辩说,"为什么你会这样做呢?" .但是你永远不知道程序员会做什么.即使这样,程序员也可能会在打开新连接之前关闭连接.另外,这会浪费应用程序的资源.如果你真的想要有两个或更多的开放连接,请停在这里,这将是每个用户一个连接的实现.
为了使它成为单一点,我们必须将此类转换为单个类.单例是一种设计模式,它允许我们拥有任何给定对象的一个且只有一个实例.所以,让它成为一个单身人士!
public构造函数转换为私有构造函数.我们必须只给那个叫它的人一个实例.在DAOManager随后变成了工厂!private实际存储单例的新类.getInstance()方法来为我们提供一个可以调用的单例实例.让我们看看它是如何实现的.
public class DAOManager {
public static DAOManager getInstance() {
return DAOManagerSingleton.INSTANCE;
}
public void open() throws SQLException {
try
{
if(this.con==null || !this.con.isOpen())
this.con = src.getConnection();
}
catch(SQLException e) { throw e; }
}
public void close() throws SQLException {
try
{
if(this.con!=null && this.con.isOpen())
this.con.close();
}
catch(SQLException e) { throw e; }
}
//Private
private DataSource src;
private Connection con;
private DAOManager() throws Exception {
try
{
InitialContext ctx = new InitialContext();
this.src = (DataSource)ctx.lookup("jndi/MYSQL");
}
catch(Exception e) { throw e; }
}
private static class DAOManagerSingleton {
public static final DAOManager INSTANCE;
static
{
DAOManager dm;
try
{
dm = new DAOManager();
}
catch(Exception e)
dm = null;
INSTANCE = dm;
}
}
}
Run Code Online (Sandbox Code Playgroud)
当应用程序启动时,只要有人需要单例,系统就会实例化一个DAOManager.非常简洁,我们创建了一个单一的接入点!
但是单身是反模式的原因! 我知道有些人不会喜欢单身人士.然而,它解决了这个问题(并且已经解决了我的问题)相当不错.这只是实现此解决方案的一种方式,如果您有其他方式,欢迎您这样做.
是的,的确有.单例将为整个应用程序创建一个实例!这在许多层面都是错误的,特别是如果我们有一个网络系统,我们的应用程序将是多线程的!那么我们如何解决这个问题呢?
Java提供了一个名为的类ThreadLocal.一个ThreadLocal变量都会有每个线程一个实例.嘿,它解决了我们的问题!详细了解它的工作原理,您需要了解其目的,以便我们继续.
让我们来做吧INSTANCE ThreadLocal.以这种方式修改类:
public class DAOManager {
public static DAOManager getInstance() {
return DAOManagerSingleton.INSTANCE.get();
}
public void open() throws SQLException {
try
{
if(this.con==null || !this.con.isOpen())
this.con = src.getConnection();
}
catch(SQLException e) { throw e; }
}
public void close() throws SQLException {
try
{
if(this.con!=null && this.con.isOpen())
this.con.close();
}
catch(SQLException e) { throw e; }
}
//Private
private DataSource src;
private Connection con;
private DAOManager() throws Exception {
try
{
InitialContext ctx = new InitialContext();
this.src = (DataSource)ctx.lookup("jndi/MYSQL");
}
catch(Exception e) { throw e; }
}
private static class DAOManagerSingleton {
public static final ThreadLocal<DAOManager> INSTANCE;
static
{
ThreadLocal<DAOManager> dm;
try
{
dm = new ThreadLocal<DAOManager>(){
@Override
protected DAOManager initialValue() {
try
{
return new DAOManager();
}
catch(Exception e)
{
return null;
}
}
};
}
catch(Exception e)
dm = null;
INSTANCE = dm;
}
}
}
Run Code Online (Sandbox Code Playgroud)
我真的很想不要这样做
catch(Exception e)
{
return null;
}
Run Code Online (Sandbox Code Playgroud)
但initialValue()不能抛出异常.哦,initialValue()你的意思是?此方法将告诉我们该ThreadLocal变量将保持什么值.基本上我们正在初始化它.所以,多亏了这一点,我们现在每个线程可以有一个实例.
如果DAOManager没有DAO,A 就没有了.所以我们至少应该创建几个.
DAO是"数据访问对象"的缩写,是一种设计模式,它将管理数据库操作的责任权交给代表某个表的类.
为了DAOManager更有效地使用我们,我们将定义一个GenericDAO抽象的DAO,它将保存所有DAO之间的公共操作.
public abstract class GenericDAO<T> {
public abstract int count() throws SQLException;
//Protected
protected final String tableName;
protected Connection con;
protected GenericDAO(Connection con, String tableName) {
this.tableName = tableName;
this.con = con;
}
}
Run Code Online (Sandbox Code Playgroud)
现在,这就足够了.让我们创建一些DAO.假设我们有两个POJO:First并且Second,只有一个String名为的字段data及其getter和setter.
public class FirstDAO extends GenericDAO<First> {
public FirstDAO(Connection con) {
super(con, TABLENAME);
}
@Override
public int count() throws SQLException {
String query = "SELECT COUNT(*) AS count FROM "+this.tableName;
PreparedStatement counter;
try
{
counter = this.con.PrepareStatement(query);
ResultSet res = counter.executeQuery();
res.next();
return res.getInt("count");
}
catch(SQLException e){ throw e; }
}
//Private
private final static String TABLENAME = "FIRST";
}
Run Code Online (Sandbox Code Playgroud)
SecondDAO将具有或多或少相同的结构,只是TABLENAME改为"SECOND".
DAOManager不仅应该服务于作为单一连接点的目的.实际上,DAOManager应该回答这个问题:
谁负责管理与数据库的连接?
各个DAO不应该管理它们,但是DAOManager.我们已经部分回答了这个问题,但现在我们不应该让任何人管理与数据库的其他连接,甚至不管DAO.但是,DAO需要连接到数据库!谁应该提供它?DAOManager确实!我们应该做的是在里面制作一个工厂方法DAOManager.不仅如此,DAOManager还会将当前连接交给他们!
Factory是一种设计模式,它允许我们创建某个超类的实例,而不确切知道将返回哪个子类.
首先,让我们创建一个enum列表.
public enum Table { FIRST, SECOND }
Run Code Online (Sandbox Code Playgroud)
而现在,工厂方法里面DAOManager:
public GenericDAO getDAO(Table t) throws SQLException
{
try
{
if(this.con == null || this.con.isClosed()) //Let's ensure our connection is open
this.open();
}
catch(SQLException e){ throw e; }
switch(t)
{
case FIRST:
return new FirstDAO(this.con);
case SECOND:
return new SecondDAO(this.con);
default:
throw new SQLException("Trying to link to an unexistant table.");
}
}
Run Code Online (Sandbox Code Playgroud)
我们现在很高兴.请尝试以下代码:
DAOManager dao = DAOManager.getInstance();
FirstDAO fDao = (FirstDAO)dao.getDAO(Table.FIRST);
SecondDAO sDao = (SecondDAO)dao.getDAO(Table.SECOND);
System.out.println(fDao.count());
System.out.println(sDao.count());
dao.close();
Run Code Online (Sandbox Code Playgroud)
是不是很花哨,容易阅读?不只是,但是当你打电话时close(),你关闭了DAO正在使用的每一个连接.但是如何?!好吧,他们共享相同的连接,所以这很自然.
我们可以从这里做几件事.要确保关闭连接并返回池,请执行以下操作DAOManager:
@Override
protected void finalize()
{
try{ this.close(); }
finally{ super.finalize(); }
}
Run Code Online (Sandbox Code Playgroud)
您也可以实现封装方法setAutoCommit(),commit()并rollback()从Connection这样你就可以有一个更好的处理您的交易.我也做的是,而不是只持有一个Connection,DAOManager也持有一个PreparedStatement和一个ResultSet.因此,当close()它调用它时也会关闭它们.关闭语句和结果集的快捷方法!
我希望本指南在您的下一个项目中对您有用!
我认为如果你想在普通JDBC中做一个简单的DAO模式,你应该保持简单:
public List<Customer> listCustomers() {
List<Customer> list = new ArrayList<>();
try (Connection conn = getConnection();
Statement s = conn.createStatement();
ResultSet rs = s.executeQuery("select * from customers")) {
while (rs.next()) {
list.add(processRow(rs));
}
return list;
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e); //or your exceptions
}
}
Run Code Online (Sandbox Code Playgroud)
您可以在名为CustomersDao或CustomerManager的类中遵循此模式,您可以使用简单的方法调用它
CustomersDao dao = new CustomersDao();
List<Customers> customers = dao.listCustomers();
Run Code Online (Sandbox Code Playgroud)
请注意,我正在尝试使用资源,这个代码对于连接泄漏,干净和简单是安全的,您可能不希望使用Factorys,接口以及在许多情况下不会发生的所有管道的完整DAO模式增加实际价值.
我认为使用ThreadLocals不是一个好主意,在接受的答案中使用的Bad是类加载器泄漏的来源
记住始终在try finally块中关闭您的资源(Statements,ResultSet,Connections)或使用try with resources
| 归档时间: |
|
| 查看次数: |
62592 次 |
| 最近记录: |