2012-02-25 204 views
3

我在写一个servlet,通过访问和修改数据库中的某些表来处理每个请求。我希望与数据库的连接是线程安全的。我不想使用已有的库/框架(spring,hibernate等)。Java线程安全数据库连接

我知道我可以通过以下方式使用Java的ThreadLocal的这个:

public class DatabaseRegistry { //assume it's a singleton 


private Properties prop = new Properties(); 
    public static final ThreadLocal<Connection> threadConnection = new ThreadLocal<Connection>(); 

    private Connection connect() throws SQLException { 
     try { 
      // This will load the MySQL driver, each DB has its own driver 
      Class.forName("com.mysql.jdbc.Driver"); 
      // Setup the connection with the DB 
      Connection connection = DriverManager 
        .getConnection("jdbc:mysql://" + prop.getProperty("hostname") + "/" + prop.getProperty("database") + "?" 
          + "user=" + prop.getProperty("username") + "&password=" + prop.getProperty("password")); 
      return connection; 
     } catch (SQLException e) {   
      throw e; 
     } catch (ClassNotFoundException e) { 
      e.printStackTrace(); 
     } 

     return null; 

    } 

    public Connection getConnection() throws SQLException { 

     if(threadConnection.get() == null) { 
      Connection connection = connect(); 
      threadConnection.set(connection); 
      return threadConnection.get(); 
     } else 
      return threadConnection.get(); 
    } 

    private void freeConnection(Connection connection) throws SQLException { 
     connection.close(); 
     threadConnection.remove(); 
    } 
} 

每次调用的getConnection(),新的连接被添加到ThreadLocal的对象,然后当你释放连接删除。

这是这样做的正确方法还是应该DatabaseRegistry本身扩展ThreadLocal类?还是有更好的方法来做到这一点,使所有连接线程安全?

谢谢

+0

http://stackoverflow.com/questions/1209693/is-mysql-connector-jdbc-thread-safe – 2012-02-25 16:11:43

+0

我认为这不是一个好的做法。请使用连接池,它将保留可用连接的核心大小。如果你使用ThreadLocal,每个请求都将是自己的一个连接,如果你的web服务器是阻塞的,连接将不会按时发布。 – 2015-03-20 02:26:17

回答

2

我不认为这使得数据库连接线程安全是一种常见的做法。通常你想要的是两种:

  • 序列化访问你的servlet的某些部分,以便有在同一时间(如实施SingleThreadModel接口)不超过一个的servlet中执行代码。
  • 锁定特定的表/页面/行,以便您可以对某些特定的元组(通过更改数据库隔离级别)进行操作。
  • 使用乐观锁定来检测表中的修改行(使用表的某些引用属性来检查当前版本是否与表中的行相同)。

据我所知,典型的使用ThreadLocal的是存储每个线程唯一的数据库连接,让相同的连接可以用不同的方法来使用你的业务逻辑,而不需要每次都将它作为一个参数。由于常见的servlet容器实现使用线程来填充HTTP请求,因此两个不同的请求将保证使用两个不同的数据库连接。

+0

您最近的评论适用于此。我通过单个请求使用连接。每个请求都可以通过很多调用来访问需要连接的功能。例如,我将使用在URL中传递的ID通过Finder类在数据库中查找域对象。然后,我将编辑该对象并通过其他类将其更新到数据库中。虽然我每次都关闭结果集,但我希望连接是相同的。 – 2012-02-25 04:39:09

+0

我仍然认为这是在重塑车轮。如果要在容器中运行servlet(如Tomcat),请使用JNDI查找数据源并将数据源配置为为您建立池连接。那里有很多例子。 – dbrin 2012-02-25 05:38:10

+0

是不是什么SQL DriverManager当你做getConnection(); – 2012-02-25 05:47:17

0

我不确定为什么你想要你的数据库连接是线程安全的。大多数时间建立与数据库的连接是交易中最长的部分。通常连接在请求和开放连接池之间重复使用(通过框架或更典型的是应用程序服务器)。

如果你担心并发修改到同一个表,你可能想看看在同步方法:http://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html

+1

-1,使用Java同步来保护数据库事务不发生碰撞是一个**糟糕的**想法 – 2012-02-25 04:28:31

+0

我不会在一个普通的应用程序中这样做,但它听起来像这是某种小型的项目,他想要的东西不使用框架并且想要在内部管理连接池。在这种情况下,这不是一个坏主意。是的,你会等待其他请求,但它会完成肮脏的工作。 – dbrin 2012-02-25 05:44:22

+0

这样做“正确的方式”并不是那么难,而且这教导了可怕的习惯。它还留下了大量的代码债务,使得几乎不可能扩大应用范围。 – 2012-02-25 18:55:54

2

我知道你说你不想用库来做到这一点,但如果你这样做,你会变得更好。选择一个标准的连接池(C3P0,DBCP,或其他),你会比你自己烘烤更快乐。为什么你不能用图书馆来做到这一点?