JDBC 数据库连接池
1、数据库连接池
1.1、概念
其实就是一个容器(集合),存放数据库连接的容器。
当系统初始化好后,容器被创建,容器中会申请一些连接对象,当用户来访问数据库时,从容器中获取连接对象,用户访问完之后,会将连接对象归还给容器。
好处:
- 节约资源
- 用户访问高效
1.2、实现
- 标准接口:
DataSource
javax.sql 包下的- 方法:
- 获取连接:
getConnection()
- 归还连接:
Connection.close()
。如果连接对象Connection 是从连接池中获取的,那么调用Connection.close()
方法,则不会再关闭连接了。而是归还连接。
- 获取连接:
- 方法:
- 一般我们不去实现它,有数据库厂商来实现
- C3P0:数据库连接池技术
- Druid:数据库连接池实现技术,由阿里巴巴提供的
1.3、C3P0:数据库连接池技术
步骤:
- 导入 jar 包 (两个)
c3p0-0.9.5.2.jar
mchange-commons-java-0.2.12.jar
- 不要忘记导入数据库驱动 jar 包
- 定义配置文件:
- 名称:
c3p0.properties
或者c3p0-config.xml
- 路径:直接将文件放在 src 目录下即可
- 名称:
- 创建核心对象:数据库连接池对象
ComboPooledDataSource
- 获取连接:
getConnection
package cn.itcast.datasource.c3p0;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
/**
* C3p0 演示
*/
public class c3p0Demo {
public static void main(String[] args) throws SQLException {
//1.创建数据库连接池对象
DataSource ds = new ComboPooledDataSource();
//2.获取连接对象
Connection conn = ds.getConnection();
//3.打印
System.out.println(conn);
}
}
9月 29, 2021 3:15:02 下午 com.mchange.v2.log.MLog
信息: MLog clients using java 1.4+ standard logging.
9月 29, 2021 3:15:03 下午 com.mchange.v2.c3p0.C3P0Registry
信息: Initializing c3p0-0.9.5.2 [built 08-December-2015 22:06:04 -0800; debug? true; trace: 10]
9月 29, 2021 3:15:03 下午 com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource
信息: Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 3000, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, contextClassLoaderSource -> caller, dataSourceName -> 1hgf3ltakdfiry4qwe93a|534df152, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.jdbc.Driver, extensions -> {}, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, forceSynchronousCheckins -> false, forceUseNamedDriverClass -> false, identityToken -> 1hgf3ltakdfiry4qwe93a|534df152, idleConnectionTestPeriod -> 0, initialPoolSize -> 5, jdbcUrl -> jdbc:mysql://192.168.31.140:3306/db3, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 10, maxStatements -> 0, maxStatementsPerConnection -> 0, minPoolSize -> 3, numHelperThreads -> 3, preferredTestQuery -> null, privilegeSpawnedThreads -> false, properties -> {password=******, user=******}, propertyCycle -> 0, statementCacheNumDeferredCloseThreads -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, userOverrides -> {}, usesTraditionalReflectiveProxies -> false ]
com.mchange.v2.c3p0.impl.NewProxyConnection@1e683a3e [wrapping: com.mysql.jdbc.JDBC4Connection@2053d869]
1.3.1、C3P0 的配置文件
<c3p0-config>
<!-- 使用默认的配置读取连接池对象 -->
<default-config>
<!-- 连接参数 -->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://192.168.31.140:3306/db3</property>
<property name="user">root</property>
<property name="password">Opfordream@0518</property>
<!-- 连接池参数 -->
<!-- 初始化连接数量 -->
<property name="initialPoolSize">5</property>
<!-- 最大连接数量 -->
<property name="maxPoolSize">10</property>
<!-- 超时时间 -->
<property name="checkoutTimeout">3000</property>
</default-config>
<named-config name="otherc3p0">
<!-- 连接参数 -->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://192.168.31.140:3306/db3</property>
<property name="user">root</property>
<property name="password">Opfordream@0518</property>
<!-- 连接池参数 -->
<property name="initialPoolSize">5</property>
<property name="maxPoolSize">8</property>
<property name="checkoutTimeout">1000</property>
</named-config>
</c3p0-config>
验证配置文件的参数:
package cn.itcast.datasource.c3p0;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
/**
* c3p0 配置文件参数验证
*/
public class c3p0Demo2 {
public static void main(String[] args) throws SQLException {
//1.获取 DataSource. 使用默认配置
DataSource ds = new ComboPooledDataSource();
//2.获取连接
for (int i = 1; i <= 11; i++) {
Connection conn = ds.getConnection();
System.out.println(i + ":" + conn);
if (i == 5){
conn.close(); //归还连接到连接池
}
}
System.out.println("-----------------------------------------------");
testNamedConfig();
}
public static void testNamedConfig() throws SQLException {
//1.1 获取 DataSource. 使用指定名称配置
DataSource ds = new ComboPooledDataSource("otherc3p0");
//2.获取连接
for (int i = 1; i <= 10; i++) {
Connection conn = ds.getConnection();
System.out.println(i + ":" + conn);
}
}
}
9月 29, 2021 3:18:03 下午 com.mchange.v2.log.MLog
信息: MLog clients using java 1.4+ standard logging.
9月 29, 2021 3:18:04 下午 com.mchange.v2.c3p0.C3P0Registry
信息: Initializing c3p0-0.9.5.2 [built 08-December-2015 22:06:04 -0800; debug? true; trace: 10]
9月 29, 2021 3:18:04 下午 com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource
信息: Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 3000, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, contextClassLoaderSource -> caller, dataSourceName -> 1hgf3ltakdfmnmf12hmy87|534df152, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.jdbc.Driver, extensions -> {}, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, forceSynchronousCheckins -> false, forceUseNamedDriverClass -> false, identityToken -> 1hgf3ltakdfmnmf12hmy87|534df152, idleConnectionTestPeriod -> 0, initialPoolSize -> 5, jdbcUrl -> jdbc:mysql://192.168.31.140:3306/db3, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 10, maxStatements -> 0, maxStatementsPerConnection -> 0, minPoolSize -> 3, numHelperThreads -> 3, preferredTestQuery -> null, privilegeSpawnedThreads -> false, properties -> {password=******, user=******}, propertyCycle -> 0, statementCacheNumDeferredCloseThreads -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, userOverrides -> {}, usesTraditionalReflectiveProxies -> false ]
1:com.mchange.v2.c3p0.impl.NewProxyConnection@63a12c68 [wrapping: com.mysql.jdbc.JDBC4Connection@28f3b248]
2:com.mchange.v2.c3p0.impl.NewProxyConnection@32b260fa [wrapping: com.mysql.jdbc.JDBC4Connection@581ac8a8]
3:com.mchange.v2.c3p0.impl.NewProxyConnection@57d7f8ca [wrapping: com.mysql.jdbc.JDBC4Connection@76c3e77a]
4:com.mchange.v2.c3p0.impl.NewProxyConnection@67c33749 [wrapping: com.mysql.jdbc.JDBC4Connection@fba92d3]
5:com.mchange.v2.c3p0.impl.NewProxyConnection@fa49800 [wrapping: com.mysql.jdbc.JDBC4Connection@71238fc2]
6:com.mchange.v2.c3p0.impl.NewProxyConnection@5fbdfdcf [wrapping: com.mysql.jdbc.JDBC4Connection@4efc180e]
7:com.mchange.v2.c3p0.impl.NewProxyConnection@25084a1e [wrapping: com.mysql.jdbc.JDBC4Connection@71238fc2]
8:com.mchange.v2.c3p0.impl.NewProxyConnection@3bf9ce3e [wrapping: com.mysql.jdbc.JDBC4Connection@16610890]
9:com.mchange.v2.c3p0.impl.NewProxyConnection@383bfa16 [wrapping: com.mysql.jdbc.JDBC4Connection@4d465b11]
10:com.mchange.v2.c3p0.impl.NewProxyConnection@5562c41e [wrapping: com.mysql.jdbc.JDBC4Connection@32ee6fee]
11:com.mchange.v2.c3p0.impl.NewProxyConnection@7133da86 [wrapping: com.mysql.jdbc.JDBC4Connection@3232a28a]
-----------------------------------------------
9月 29, 2021 3:18:04 下午 com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource
信息: Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 1000, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, contextClassLoaderSource -> caller, dataSourceName -> otherc3p0, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.jdbc.Driver, extensions -> {}, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, forceSynchronousCheckins -> false, forceUseNamedDriverClass -> false, identityToken -> 1hgf3ltakdfmnmf12hmy87|73e22a3d, idleConnectionTestPeriod -> 0, initialPoolSize -> 5, jdbcUrl -> jdbc:mysql://192.168.31.140:3306/db3, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 8, maxStatements -> 0, maxStatementsPerConnection -> 0, minPoolSize -> 3, numHelperThreads -> 3, preferredTestQuery -> null, privilegeSpawnedThreads -> false, properties -> {password=******, user=******}, propertyCycle -> 0, statementCacheNumDeferredCloseThreads -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, userOverrides -> {}, usesTraditionalReflectiveProxies -> false ]
1:com.mchange.v2.c3p0.impl.NewProxyConnection@35aea049 [wrapping: com.mysql.jdbc.JDBC4Connection@7205765b]
2:com.mchange.v2.c3p0.impl.NewProxyConnection@22ef9844 [wrapping: com.mysql.jdbc.JDBC4Connection@6283d8b8]
3:com.mchange.v2.c3p0.impl.NewProxyConnection@3f6b0be5 [wrapping: com.mysql.jdbc.JDBC4Connection@611889f4]
4:com.mchange.v2.c3p0.impl.NewProxyConnection@48f278eb [wrapping: com.mysql.jdbc.JDBC4Connection@2f217633]
5:com.mchange.v2.c3p0.impl.NewProxyConnection@1a18644 [wrapping: com.mysql.jdbc.JDBC4Connection@5acf93bb]
6:com.mchange.v2.c3p0.impl.NewProxyConnection@6cd28fa7 [wrapping: com.mysql.jdbc.JDBC4Connection@614ca7df]
7:com.mchange.v2.c3p0.impl.NewProxyConnection@66d3eec0 [wrapping: com.mysql.jdbc.JDBC4Connection@1e04fa0a]
8:com.mchange.v2.c3p0.impl.NewProxyConnection@18d87d80 [wrapping: com.mysql.jdbc.JDBC4Connection@618425b5]
Exception in thread "main" java.sql.SQLException: An attempt by a client to checkout a Connection has timed out.
at com.mchange.v2.sql.SqlUtils.toSQLException(SqlUtils.java:118)
at com.mchange.v2.sql.SqlUtils.toSQLException(SqlUtils.java:77)
at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.checkoutPooledConnection(C3P0PooledConnectionPool.java:690)
at com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource.getConnection(AbstractPoolBackedDataSource.java:140)
at cn.itcast.datasource.c3p0.c3p0Demo2.testNamedConfig(c3p0Demo2.java:38)
at cn.itcast.datasource.c3p0.c3p0Demo2.main(c3p0Demo2.java:29)
Caused by: com.mchange.v2.resourcepool.TimeoutException: A client timed out while waiting to acquire a resource from com.mchange.v2.resourcepool.BasicResourcePool@20ccf40b -- timeout at awaitAvailable()
at com.mchange.v2.resourcepool.BasicResourcePool.awaitAvailable(BasicResourcePool.java:1467)
at com.mchange.v2.resourcepool.BasicResourcePool.prelimCheckoutResource(BasicResourcePool.java:644)
at com.mchange.v2.resourcepool.BasicResourcePool.checkoutResource(BasicResourcePool.java:554)
at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.checkoutAndMarkConnectionInUse(C3P0PooledConnectionPool.java:758)
at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.checkoutPooledConnection(C3P0PooledConnectionPool.java:685)
... 3 more
1.4、Druid:数据库连接池实现技术
步骤:
- 导入 jar 包
druid-1.0.9.jar
- 定义配置文件:
- 是 properties 形式的
- 可以叫任意名称,可以放在任意目录下
- 加载配置文件。
Properties
- 获取数据库连接池对象:通过工厂类来获取
DruidDataSourceFactory
- 获取连接:
getConnection
package cn.itcast.datasource.druid;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;
/**
* Druid 演示
*/
public class DruidDemo {
public static void main(String[] args) throws Exception {
//1.导入 jar 包
//2.定义配置文件
//3.加载配置文件
Properties pro = new Properties();
InputStream is = DruidDemo.class.getClassLoader().getResourceAsStream("druid.properties");
pro.load(is);
//4.获取连接池对象
DataSource ds = DruidDataSourceFactory.createDataSource(pro);
//5.获取连接
Connection conn = ds.getConnection();
System.out.println(conn);
}
}
9月 29, 2021 3:21:06 下午 com.alibaba.druid.pool.DruidDataSource info
信息: {dataSource-1} inited
com.mysql.jdbc.JDBC4Connection@5c90e579
1.4.1、定义工具类
- 定义一个类
JDBCUtils
- 提供静态代码块加载配置文件,初始化连接池对象
- 提供方法
- 获取连接方法:通过数据库连接池获取连接
- 释放资源
- 获取连接池的方法
package cn.itcast.utils;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
/**
* Druid 连接池的工具类
*/
public class JDBCUtils {
//1. 定义成员变量 DataSource
private static DataSource ds;
static {
try {
//1.加载配置文件
Properties pro = new Properties();
pro.load(JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties"));
//2.获取 DataSource
ds = DruidDataSourceFactory.createDataSource(pro);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取连接
*/
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
/**
* 释放资源
*/
public static void close(Statement stmt, Connection conn){
if (stmt != null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void close(ResultSet rs,Statement stmt, Connection conn){
if (rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (stmt != null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
/**
* 获取连接池的方法
*/
public static DataSource getDataSource(){
return ds;
}
}
package cn.itcast.datasource.druid;
import cn.itcast.utils.JDBCUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* 使用新的工具类
*/
public class DruidDemo2 {
public static void main(String[] args) {
/**
* 完成一个添加的操作,给 account 添加一条记录
*/
Connection conn = null;
PreparedStatement pstmt = null;
try {
//1.获取连接
conn = JDBCUtils.getConnection();
//2.定义 sql
String sql = "insert into account values(null, ?, ?)";
//3.获取 pstmt 对象
pstmt = conn.prepareStatement(sql);
//给 ? 赋值
pstmt.setString(1, "wangwu");
pstmt.setDouble(2, 3000);
//4.执行 sql
int count = pstmt.executeUpdate();
System.out.println(count);
} catch (SQLException e) {
e.printStackTrace();
}finally {
JDBCUtils.close(pstmt, conn);
}
}
}
9月 29, 2021 3:25:48 下午 com.alibaba.druid.pool.DruidDataSource info
信息: {dataSource-1} inited
1
2、Spring JDBC
Spring 框架对 JDBC 的简单封装。提供了一个 JDBCTemplate 对象简化 JDBC 的开发。
步骤:
-
导入 jar 包
-
创建 JdbcTemplate 对象。依赖于数据源 DataSource
JdbcTemplate template = new JdbcTemplate(ds);
-
调用 JdbcTemplate 的方法来完成 CRUD 的操作
-
update()
:执行 DML 语句。增、删、改语句 -
queryForMap()
:查询结果将结果集封装为 map 集合,将列名作为 key,将值作为 value 将这条记录封装为一个map 集合注意:这个方法查询的结果集长度只能是 1
-
queryForList()
:查询结果将结果集封装为 list 集合注意:将每一条记录封装为一个 Map 集合,再将 Map 集合装载到 List 集合中
-
query()
:查询结果,将结果封装为 JavaBean 对象- query 的参数:
RowMapper
- 一般我们使用 BeanPropertyRowMapper 实现类。可以完成数据到 JavaBean 的自动封装
new BeanPropertyRowMapper<类型>(类型.class)
- query 的参数:
-
queryForObject()
:查询结果,将结果封装为对象一般用于聚合函数的查询
-
练习
需求:
- 修改 1 号数据的 salary 为 10000
- 添加一条记录
- 删除刚才添加的记录
- 查询 id 为 1 的记录,将其封装为 Map 集合
- 查询所有记录,将其封装为 List
- 查询所有记录,将其封装为 Emp 对象的 List 集合
- 查询总记录数
package JdbcTemplate;
import JdbcTemplate.domain.Emp;
import cn.itcast.utils.JDBCUtils;
import org.junit.Test;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import java.sql.Date;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
public class JdbcTemplateDemo2 {
//1.获取 JDBCTemplate 对象
private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
//Junit 单元测试,可以让方法独立执行
/**
* 1. 修改1号数据的 salary 为 10000
*/
@Test
public void test1() {
//2.定义 sql
String sql = "update emp set salary = 10000 where id = 1001";
//3.执行 sql
int count = template.update(sql);
System.out.println(count);
}
/**
*2. 添加一条记录
*/
@Test
public void test2(){
//2.定义 sql
String sql = "insert into emp(id, ename, dept_id) values(?, ?, ?) ";
//3.执行 sql
int count = template.update(sql, 1015, "郭靖", 10);
System.out.println(count);
}
/**
*3. 删除刚才添加的记录
*/
@Test
public void test3(){
//2.定义 sql
String sql = "delete from emp where id = ?";
//3.执行 sql
int count = template.update(sql, 1015);
System.out.println(count);
}
/**
*4. 查询id为1的记录,将其封装为Map集合
* 注意,这个方法查询的结果集长度只能是 1
*/
@Test
public void test4(){
//2.定义 sql
String sql = "select * from emp where id = ? /*or id = ?*/";
//3.执行 sql
Map<String, Object> map = template.queryForMap(sql, 1001/*, 1002*/);
System.out.println(map);
//{id=1001, ename=孙悟空, job_id=4, mgr=1004, joindate=2000-12-17, salary=10000.00, bonus=null, dept_id=20}
}
/**
* 5. 查询所有记录,将其封装为List
*/
@Test
public void test5(){
//2.定义 sql
String sql = "select * from emp";
//3.执行 sql
List<Map<String, Object>> list = template.queryForList(sql);
for (Map<String, Object> stringObjectMap : list) {
System.out.println(stringObjectMap);
}
}
/**
* 6. 查询所有记录,将其封装为Emp对象的List集合
*/
@Test
public void test6(){
//2.定义 sql
String sql = "select * from emp";
//3.执行 sql
List<Emp> list = template.query(sql, new RowMapper<Emp>() {
@Override
public Emp mapRow(ResultSet rs, int i) throws SQLException {
Emp emp = new Emp();
int id = rs.getInt("id");
String ename = rs.getString("ename");
int job_id = rs.getInt("job_id");
int mgr = rs.getInt("mgr");
Date join_date = rs.getDate("joindate");
double salary = rs.getDouble("salary");
double bonus = rs.getDouble("bonus");
int dept_id = rs.getInt("dept_id");
emp.setId(id);
emp.setEname(ename);
emp.setJob_id(job_id);
emp.setMgr(mgr);
emp.setJoindate(join_date);
emp.setSalary(salary);
emp.setBonus(bonus);
emp.setDept_id(dept_id);
return emp;
}
});
for (Emp emp: list) {
System.out.println(emp);
}
}
/**
* 6. 查询所有记录,将其封装为Emp对象的List集合
*/
@Test
public void test6_1(){
//2.定义 sql
String sql = "select * from emp";
//3.执行 sql
List<Emp> list = template.query(sql, new BeanPropertyRowMapper<Emp>(Emp.class));
for (Emp emp: list) {
System.out.println(emp);
}
}
/**
* 7. 查询总记录数
*/
@Test
public void test7(){
//2.定义 sql
String sql = "select count(id) from emp";
//3.执行 sql
Long total = template.queryForObject(sql, Long.class);
System.out.println(total);
}
}