我有一个Postgres 9.6.17 DBMS和一个简单的Java独立程序(没有Spring),该程序连接到DBMS并发出查询。根据Postgres文档,可以在“简单模式”或“扩展模式”(其中解析和查询重写一次完成)中执行查询,但是绑定重复发生。
我有一个简单的表customer(cust_id integer, first_name varchar(100), last_name varchar(100))
。我正在执行的代码如下:
Properties props = new Properties();
....//typical properties such as user, password, database etc
props.setProperty("preferQueryMode", "extendedForPrepared");
Connection con = DriverManager.getConnection(url, props);
PreparedStatement st = con.prepareStatement("select * from ecommerce.customer where cust_id = ?");
st.setInt(1, 22);
ResultSet rs = st.executeQuery();
我希望这会在JDBC客户端和DBMS后端之间产生“扩展查询模式”协议。但是,它始终产生“简单查询模式”交互。我通过为PostgreSQL驱动程序启用JDBC日志记录来验证了这一点。我还查看了JDBC驱动程序中Query
接口的实现,我只能找到3种实现,如下所示:
BatchedQuery
CompositeQuery
SimpleQuery
尽管名称为CompositeQuery
,但所做的只是将涉及多个语句的查询拆分为SimpleQuery
对象的列表。
所以,我的问题是如何使我的扩展模式连接使用的协议?
默认情况下,在使用准备好的语句5次之后,Postgres JDBC驱动程序只会开始使用服务器端语句。从JDBC driver property documentation:
prepareThreshold = int
确定在切换为使用服务器端准备好的语句之前所需的PreparedStatement执行次数。默认值为5,这意味着在同一PreparedStatement对象的第五次执行时开始使用服务器端的预处理语句。有关服务器端预备语句的更多信息,请参见“服务器预备语句”一节。
但是该属性可以更改。
这是我测试过的程序:
Properties props = new Properties();
props.setProperty("user", "test");
props.setProperty("password", "test");
props.setProperty("loggerLevel", "TRACE");
props.setProperty("prepareThreshold", "1");
try (Connection con = DriverManager.getConnection("jdbc:postgresql:test", props);
PreparedStatement stat = con.prepareStatement("select * from app_user where username = ?"))
{
for (String username : List.of("user1", "user2", "user3"))
{
stat.setString(1, username);
try (ResultSet rs = stat.executeQuery())
{
if (rs.next())
System.out.println("User " + username + " has ID: " + rs.getString(1));
}
}
}
[prepareThreshold
设置为1
时,日志显示:
Feb 18, 2020 1:56:59 PM org.postgresql.core.v3.QueryExecutorImpl execute
FINEST: simple execute, handler=org.postgresql.jdbc.PgStatement$StatementResultHandler@33065d67, maxRows=0, fetchSize=0, flags=16
Feb 18, 2020 1:56:59 PM org.postgresql.core.v3.QueryExecutorImpl sendParse
FINEST: FE=> Parse(stmt=S_1,query="select * from app_user where username = $1",oids={1043})
Feb 18, 2020 1:56:59 PM org.postgresql.core.v3.QueryExecutorImpl sendBind
FINEST: FE=> Bind(stmt=S_1,portal=null,$1=<'user1'>)
Feb 18, 2020 1:56:59 PM org.postgresql.core.v3.QueryExecutorImpl sendDescribePortal
FINEST: FE=> Describe(portal=null)
Feb 18, 2020 1:56:59 PM org.postgresql.core.v3.QueryExecutorImpl sendExecute
FINEST: FE=> Execute(portal=null,limit=0)
...
Feb 18, 2020 1:56:59 PM org.postgresql.core.v3.QueryExecutorImpl execute
FINEST: simple execute, handler=org.postgresql.jdbc.PgStatement$StatementResultHandler@35dab4eb, maxRows=0, fetchSize=0, flags=16
Feb 18, 2020 1:56:59 PM org.postgresql.core.v3.QueryExecutorImpl sendBind
FINEST: FE=> Bind(stmt=S_1,portal=null,$1=<'user2'>)
Feb 18, 2020 1:56:59 PM org.postgresql.core.v3.QueryExecutorImpl sendExecute
FINEST: FE=> Execute(portal=null,limit=0)
...
Feb 18, 2020 1:56:59 PM org.postgresql.core.v3.QueryExecutorImpl execute
FINEST: simple execute, handler=org.postgresql.jdbc.PgStatement$StatementResultHandler@2d901eb0, maxRows=0, fetchSize=0, flags=16
Feb 18, 2020 1:56:59 PM org.postgresql.core.v3.QueryExecutorImpl sendBind
FINEST: FE=> Bind(stmt=S_1,portal=null,$1=<'user3'>)
Feb 18, 2020 1:56:59 PM org.postgresql.core.v3.QueryExecutorImpl sendExecute
FINEST: FE=> Execute(portal=null,limit=0)
这表明查询解析发生一次,然后有重复的绑定。
与未设置prepareThreshold
时比较:
Feb 18, 2020 2:01:25 PM org.postgresql.core.v3.QueryExecutorImpl execute
FINEST: simple execute, handler=org.postgresql.jdbc.PgStatement$StatementResultHandler@33065d67, maxRows=0, fetchSize=0, flags=17
Feb 18, 2020 2:01:25 PM org.postgresql.core.v3.QueryExecutorImpl sendParse
FINEST: FE=> Parse(stmt=null,query="select * from app_user where username = $1",oids={1043})
Feb 18, 2020 2:01:25 PM org.postgresql.core.v3.QueryExecutorImpl sendBind
FINEST: FE=> Bind(stmt=null,portal=null,$1=<'user1'>)
Feb 18, 2020 2:01:25 PM org.postgresql.core.v3.QueryExecutorImpl sendDescribePortal
FINEST: FE=> Describe(portal=null)
Feb 18, 2020 2:01:25 PM org.postgresql.core.v3.QueryExecutorImpl sendExecute
FINEST: FE=> Execute(portal=null,limit=0)
...
Feb 18, 2020 2:01:25 PM org.postgresql.core.v3.QueryExecutorImpl execute
FINEST: simple execute, handler=org.postgresql.jdbc.PgStatement$StatementResultHandler@8519cb4, maxRows=0, fetchSize=0, flags=17
Feb 18, 2020 2:01:25 PM org.postgresql.core.v3.QueryExecutorImpl sendParse
FINEST: FE=> Parse(stmt=null,query="select * from app_user where username = $1",oids={1043})
Feb 18, 2020 2:01:25 PM org.postgresql.core.v3.QueryExecutorImpl sendBind
FINEST: FE=> Bind(stmt=null,portal=null,$1=<'user2'>)
Feb 18, 2020 2:01:25 PM org.postgresql.core.v3.QueryExecutorImpl sendDescribePortal
FINEST: FE=> Describe(portal=null)
Feb 18, 2020 2:01:25 PM org.postgresql.core.v3.QueryExecutorImpl sendExecute
FINEST: FE=> Execute(portal=null,limit=0)
...
Feb 18, 2020 2:01:25 PM org.postgresql.core.v3.QueryExecutorImpl execute
FINEST: simple execute, handler=org.postgresql.jdbc.PgStatement$StatementResultHandler@35dab4eb, maxRows=0, fetchSize=0, flags=17
Feb 18, 2020 2:01:25 PM org.postgresql.core.v3.QueryExecutorImpl sendParse
FINEST: FE=> Parse(stmt=null,query="select * from app_user where username = $1",oids={1043})
Feb 18, 2020 2:01:25 PM org.postgresql.core.v3.QueryExecutorImpl sendBind
FINEST: FE=> Bind(stmt=null,portal=null,$1=<'user3'>)
Feb 18, 2020 2:01:25 PM org.postgresql.core.v3.QueryExecutorImpl sendDescribePortal
FINEST: FE=> Describe(portal=null)
Feb 18, 2020 2:01:25 PM org.postgresql.core.v3.QueryExecutorImpl sendExecute
FINEST: FE=> Execute(portal=null,limit=0)
其中有3个单独的语句进行解析和绑定。在查询5次之后,它们应该开始重新使用。
关于查询模式是'simple'还是'extended',无论日志看起来总是打印'simple execute',但是区别在于真正的简单模式,参数不会绑定在准备好的语句中,而是整个查询将以文本形式发送。在此示例中,如果preferQueryMode
属性设置为simple
,则日志显示:
Feb 18, 2020 2:06:19 PM org.postgresql.core.v3.QueryExecutorImpl execute
FINEST: simple execute, handler=org.postgresql.jdbc.PgStatement$StatementResultHandler@33065d67, maxRows=0, fetchSize=0, flags=1,041
Feb 18, 2020 2:06:19 PM org.postgresql.core.v3.QueryExecutorImpl sendSimpleQuery
FINEST: FE=> SimpleQuery(query="select * from app_user where username = 'user1'")
...
Feb 18, 2020 2:06:20 PM org.postgresql.core.v3.QueryExecutorImpl execute
FINEST: simple execute, handler=org.postgresql.jdbc.PgStatement$StatementResultHandler@28261e8e, maxRows=0, fetchSize=0, flags=1,041
Feb 18, 2020 2:06:20 PM org.postgresql.core.v3.QueryExecutorImpl sendSimpleQuery
FINEST: FE=> SimpleQuery(query="select * from app_user where username = 'user2'")
...
Feb 18, 2020 2:06:20 PM org.postgresql.core.v3.QueryExecutorImpl execute
FINEST: simple execute, handler=org.postgresql.jdbc.PgStatement$StatementResultHandler@d737b89, maxRows=0, fetchSize=0, flags=1,041
Feb 18, 2020 2:06:20 PM org.postgresql.core.v3.QueryExecutorImpl sendSimpleQuery
FINEST: FE=> SimpleQuery(query="select * from app_user where username = 'user3'")
注意此值与默认值之间的区别-没有参数,并且'user1','user2'和'user3'值将随每个查询内联发送。
我认为您真正要寻找的是立即重用服务器端语句并重新绑定参数。在这种情况下,将prepareThreshold
设置为1
将完成此工作。