【问题标题】:If I use a singleton class for a database connection, can one user close the connection for everybody?如果我使用单例类进行数据库连接,一个用户可以为所有人关闭连接吗?
【发布时间】:2011-07-04 06:04:40
【问题描述】:

我写了一个单例类来获取数据库连接。

现在我的问题是:假设有 100 个用户访问该应用程序。如果一个用户关闭连接,其他99个用户的连接是否会关闭?

这是我的示例程序,它使用单例类来获取数据库连接:

public class GetConnection {

    private GetConnection() { }

    public Connection getConnection() {
        Context ctx = new InitialContext();
        DataSource ds = ctx.lookup("jndifordbconc");
        Connection con = ds.getConnection();
        return con;
    }

    public static  GetConnection   getInstancetoGetConnection () {
        // which gives GetConnection class instance to call getConnection() on this .
    }
}

请指导我。

【问题讨论】:

  • 考虑使用连接池 (en.wikipedia.org/wiki/Connection_pool)。例如,DBCP (commons.apache.org/dbcp)。
  • @php 默认情况下,JNDI 数据源通常已经是连接池。
  • 这样实现的,它不是一个单例。这大致是一种工厂方法。你想达到什么目的?
  • 老实说,我认为这个问题与this one 重复,直到我更仔细地阅读它。建议修改标题以更好地反映这一点。

标签: java design-patterns jdbc


【解决方案1】:

只要您在getConnection() 调用中不返回相同 Connection 实例,那么就没有什么可担心的。然后,每个调用者都将获得自己的实例。到目前为止,您正在为每个 getConnection() 调用创建一个全新的连接,因此不会返回一些静态或实例变量。所以是安全的。

但是,这种方法很笨拙。它不需要是单例。助手/实用程序类也非常好。或者,如果您想要更多抽象,则可以使用抽象工厂返回的连接管理器。我只会在类初始化期间更改它以获取数据源一次,而不是每次在getConnection() 中获取。反正每次都是同一个实例。保持便宜。这是一个基本的启动示例:

public class Database {

    private static DataSource dataSource;

    static {
        try {
            dataSource = new InitialContext().lookup("jndifordbconc");
        }
        catch (NamingException e) { 
            throw new ExceptionInInitializerError("'jndifordbconc' not found in JNDI", e);
        }
    }

    public static Connection getConnection() {
        return dataSource.getConnection();
    }

}

按照正常的JDBC习惯用法如下。

public List<Entity> list() throws SQLException {
    List<Entity> entities = new ArrayList<Entity>();

    try (
        Connection connection = Database.getConnection();
        PreparedStatement statement = connection.prepareStatement("SELECT id, foo, bar FROM entity");
        ResultSet resultSet = statement.executeQuery();
    ) {
        while (resultSet.next()) {
            Entity entity = new Entity();
            entity.setId(resultSet.getLong("id"));
            entity.setFoo(resultSet.getString("foo"));
            entity.setBar(resultSet.getString("bar"));
            entities.add(entity);
        }
    }

    return entities;
}

另见:

【讨论】:

  • 谢谢 Balusc,我还有一个问题,因为您使用静态方法返回数据库连接,如果一个用户关闭,那么所有连接也将被关闭,称为静态操作系统,每个类一个.请纠正我。
  • 我相信您将静态方法与静态字段混淆了。只要不将连接分配为类的静态字段,就没有问题。
  • 我想他的意思是如果一堆类都调用Database.getConnection() 并且过了一会儿一个类在前一个调用的返回值上调用connection.close(),它不会关闭连接吗?大家?
  • @ChristopheDeTroyer:我相信您将静态方法与静态字段混淆了。只要不将连接分配为类的静态字段,就没有问题。
  • 好吧,我想你不明白我的问题。我有两个类FooBar,每个类都使用Database 类。他们打电话给Database.getConnection() 然后关闭它。因为如果Foo 关闭,它们都使用相同的连接对象实例,所以Bar 也会关闭,对吧?
【解决方案2】:

以下代码是一个有效且经过测试的 Java 单例模式。

public class Database {

    private static Database dbIsntance;
    private static Connection con ;
    private static Statement stmt;


    private Database() {
      // private constructor //
    }

    public static Database getInstance(){
    if(dbIsntance==null){
        dbIsntance= new Database();
    }
    return dbIsntance;
    }

    public  Connection getConnection(){

        if(con==null){
            try {
                String host = "jdbc:derby://localhost:1527/yourdatabasename";
                String username = "yourusername";
                String password = "yourpassword";
                con = DriverManager.getConnection( host, username, password );
            } catch (SQLException ex) {
                Logger.getLogger(Database.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

        return con;
    }

在任何类中获取连接时,只需使用以下行

Connection con = Database.getInstance().getConnection();

希望对你有帮助:)

【讨论】:

  • 使用此模式不应该始终打开连接吗?
【解决方案3】:
package es.sm2.conexion;

    import java.sql.Connection;
    import java.sql.DriverManager;

    public class ConexionTest {
        private static Connection conn = null;

        static Connection getConnection() throws Exception {
            if (conn == null) {
                String url = "jdbc:mysql://localhost:3306/";
                String dbName = "test";
                String driver = "com.mysql.jdbc.Driver";
                String userName = "userparatest";
                String password = "userparatest";

                Class.forName(driver).newInstance();
                conn = DriverManager.getConnection(url + dbName, userName, password);
            }

            return conn;
        }
    }

关闭连接

public static void closeConnection(Connection conn) {

        try {

            conn.close();

        } catch (SQLException e) {

        }

    }

调用连接:

package conexion.uno;

import java.sql.*;

import es.sm2.conexion.ConexionTest;

public class LLamadorConexion {

    public void llamada() {
        Connection conn = null;
        PreparedStatement statement = null;
        ResultSet resultado = null;
        String query = "SELECT * FROM empleados";

        try {
            conn = ConexionTest.getConnection();
            statement = conn.prepareStatement(query);
            resultado = statement.executeQuery();

            while (resultado.next()) {
                System.out.println(resultado.getString(1) + "\t" + resultado.getString(2) + "\t" + resultado.getString(3) + "\t" );
            }
        } 
        catch (Exception e) {
            System.err.println("El porque del cascar: " + e.getMessage());
        } 
        finally {
            ConexionTest.closeConnection(conn);

        }
    }
}

【讨论】:

    【解决方案4】:

    伟大的职位,farhangdon!但是,我发现它有点麻烦,因为一旦关闭连接,就没有其他方法可以开始新的连接了。一个小技巧可以解决它:

    if(con==null) 替换为if(con==null || con.isClosed())

    【讨论】:

      【解决方案5】:
      import java.sql.Connection;
      import java.sql.DriverManager;
      
      public class sql11 {
      
          static Connection getConnection() throws Exception {
              Class.forName("com.mysql.jdbc.Driver");
              Connection c = DriverManager.getConnection("jdbc:mysql://localhost:3306/ics", "root", "077");
              return c;
      
          }
      }
      

      【讨论】:

      • 您应该尝试在答案上更加表达并正确格式化代码。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-03-06
      • 1970-01-01
      • 2021-12-22
      • 1970-01-01
      • 1970-01-01
      • 2016-01-13
      • 2016-06-22
      相关资源
      最近更新 更多