提问者:小点点

如何使用准备好的语句的HikariCP连接池?


我正在开发一个Spring Boot web(REST)应用程序,我需要在其中处理许多请求。因此,我希望我的应用程序能够同时处理请求。由于 Spring Boot REST-Services 是开箱即用的并发可用的,我只需要使 (PostgreSQL) 数据库访问并发可访问。为此,我使用的是HikariCP数据源。

因为我的许多语句都是准备好的语句,所以我用一种方法收集它们,在这种方法中,我调用pstmt= connection.prepare语句("SQLCODE");为每个语句一次。当处理来自REST服务的用户交互时,这些准备好的语句将用于各种方法。

现在,当我使用HikariCP时,我不能再这样做了,可以吗?当我准备一个语句时,这个语句绑定到一个连接。如果我随后尝试并发访问它,则无法访问,因为连接未共享。

我错过了什么吗?我该如何解决这个问题?我需要从池中检索一个连接,在本地准备语句,执行查询,然后关闭连接吗?如果是这样的话,那么使用事先准备好的声明有什么意义呢(除了防止SQL注入病毒)?

我知道这些语句缓存在PostreSQL端。那么,保留准备好所有准备好的语句的方法是个好主意吗?将它们发送到数据库缓存。然后再次在本地创建相同的语句。这样,人们仍然可以利用数据库的缓存可能性。但另一方面,这将是非常丑陋的代码。

我正在使用Spring: 5.3.10, Java:11,PostgreSQL:14.0

@RestController
public class RESTController {
    
    /** The database controller. */
    private DBController dbc;
    
    /** The data source object serving as a connection pool. */
    private HikariDataSource ds;
    
    /** The logger object for this class. */
    private static Logger logger = LoggerFactory.getLogger(RESTController.class);
    
    public RESTController(DBController dbc, Config config) {
        this.dbc = dbc;

        // Create the database
        if (!this.dbc.createDB(config)) {
            logger.error("Couldn't create the database. The service will now exit.");
            Runtime.getRuntime().halt(1);
        }
        
        // Create a connection pool
        ds = new HikariDataSource();
        ds.setJdbcUrl(config.getUrl());
        ds.setUsername(config.getUser());
        ds.setPassword(config.getPassword());
        ds.addDataSourceProperty("cachePrepStmts", "true");
        ds.addDataSourceProperty("prepStmtCacheSize", "250");
        ds.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
        
        // Create the necessary tables
        if (!this.dbc.createTables(ds)) {
            logger.error("Couldn't create the tables. The service will now exit.");
            ds.close();
            Runtime.getRuntime().halt(1);
        }
        
        // Prepare SQL statements
        if (!this.dbc.prepareStatements(ds)) {
            logger.error("Couldn't prepare the SQL statements. The service will now exit.");
            ds.close();
            Runtime.getRuntime().halt(1);
        }
    }
    
    @PostMapping("/ID")
    public ResponseEntity<String> createNewDomain(@RequestParam(name = "name", required = true) String name) {
        // Do stuff ...
    }

    // [...]
}
@Component
public class DBController {

    /** The logger object for this class. */
    private static Logger logger = LoggerFactory.getLogger(DBController.class);
    
    // Prepared Statements
    private PreparedStatement stmt1, stmt2, stmt3;

    public boolean prepareStatements(HikariDataSource ds) {
        try {
            // Get connection from the pool
            Connection c = ds.getConnection();
            
            // Prepare all the statements
            stmt1 = c.prepareStatement("SQLCODE");
            stmt2 = c.prepareStatement("SQLCODE1");
            stmt2 = c.prepareStatement("SQLCODE1");
            // [...]
            
        } catch (SQLException e) {
            logger.debug("Could not prepare the SQL statements: " + e.getMessage());
            return false;
        }
        
        logger.debug("Successfully prepared the SQL statements.");
        return true;
    }

    public boolean m1(int i) {
        stmt1.setInt(i);
        ResultSet rs = stmt1.executeQuery();
    }

    public boolean m2(int j) {
        stmt1.setInt(j);
        ResultSet rs = stmt1.executeQuery();
    }

    public boolean m3(String a) {
        stmt2.setString(a);
        ResultSet rs = stmt2.executeQuery();
    }

    // [...]

}

提前感谢。


共1个答案

匿名用户

请阅读部分声明缓存在 https://github.com/brettwooldridge/HikariCP

许多连接池,包括Apache DBCP、Vibur、c3p0等都提供了PreparedStatement缓存。HikariCP没有。为什么?

所以它不缓存。如果你读了解释,也许你决定你不需要它。