一、数据库连接池

数据库连接是一种关键、有限且昂贵的资源,创建和释放数据库连接是一个很耗时的操作,频繁地进行这样的操作将占用大量的性能开销,而使用数据库连接池可以节省系统许多开销。

数据库连接池(Database Connection Pooling)在程序初始化时创建一定数量的数据库连接对象并将其保存在一块内存区中,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个;释放空闲时间超过最大空闲时间的数据库连接以避免因为没有释放数据库连接而引起的数据库连接遗漏。这项技术能明显提高对数据库操作的性能。

数据库连接池不仅负责创建数据库连接,还负责分配、管理和释放数据库连接,可以通过配置连接池的参数来控制连接池中的初始连接数、最小连接、最大连接、最大空闲时间等。

最大连接数

数据库连接池一直保持的数据库连接个数

最小连接数

限定了连接池能存储的最大连接数,如果数据库连接请求超过此数,后面的数据库连接请求将被加入到等待队列中

连接最大空闲时长

控制空闲连接存活时长,超时连接会被清理,但连接池连接总数不会低于最小连接数

最大连接时长

单个已使用连接所能持续的最大时长,超时则被释放

原理解读:程序初始化时创建一定数据量的数据库连接对象存储在内存中,当程序需要访问数据库时,并非建立一个新的连接,而是从连接池中取出一个已建立的空闲连接对象;如果连接池内没有空闲连接,连接池创建新的数据库连接对象并将这个连接对象提供给应用程序使用,应用程序用完后,数据库连接池不会立刻关闭该连接,在空闲时间超过最大空闲时间时,该连接才被关闭;当程序需要再次访问数据库时,数据库连接池会将空闲时间最长的那个连接提供给应用程序; 如果数据库连接请求超过最大连接数,则该数据库连接请求被加入到等待队列中;程序退出时,数据库连接池断开所有连接并释放资源。

流行连接池:HikariCP数据库连接池是当前速度最快的数据库连接池

代码示例

<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" lazy-init="false" destroy-method="close">
	<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
	<property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf8"></property>
	<property name="username" value="root"></property>
	<property name="password" value="root"></property>
	<!-- 连接只读数据库时配置为true, 保证安全 -->
	<property name="readOnly" value="false" /> 
	<!-- 等待连接池分配连接的最大时长(毫秒),超过这个时长还没可用的连接则发生SQLException, 缺省:30秒 -->
	<property name="connectionTimeout" value="30000" /> 
	<!-- 一个连接idle状态的最大时长(毫秒),超时则被释放(retired),缺省:10分钟 -->
	<property name="idleTimeout" value="600000" /> 
	<!-- 一个连接的生命时长(毫秒),超时而且没被使用则被释放(retired),缺省:30分钟,建议设置比数据库超时时长少30秒,参考MySQL wait_timeout参数(show variables like '%timeout%';) -->
	<property name="maxLifetime" value="1800000" /> 
	<!-- 连接池中允许的最大连接数。缺省值:10;推荐的公式:((core_count * 2) + effective_spindle_count) -->
	<property name="maximumPoolSize" value="15" />
</bean>

二、如何使用Spring JDBC

以使用HikariCP为例,要想使用Spring连接并进行数据库操作,需要使用JdbcTemplate类,借助该类提供的方法可以很方便的实现数据的增删改查。

(1)修改Spring配置文件,添加JdbcTemplate配置信息

<context:component-scan base-package="com.test"></context:component-scan>
<bean id="dateSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close" p:username="root" p:password="root">
    <property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/test"></property>
    <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
</bean>
<bean  id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" p:dataSource-ref="dateSource"></bean>

(2)创建JdbcTemplate对象,调用JdbcTemplate方法

注:可通过向操作类中添加以下代码让IoC容器自动创建JdbcTemplate对象

@Autowired
private JdbcTemplate jdbcTemplate;

1、修改

public class Test {

	public static void main(String[] args) {
		ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
		JdbcTemplate jdbcTemplate = applicationContext.getBean(JdbcTemplate.class);
		String sql = "insert into user_inf (id,name,password,address) values(?,?,?,?) ";
		int effect = jdbcTemplate.update(sql, UUID.randomUUID().toString(),"tim","111","beijing");
		
		if (effect>0) {
			System.out.println("OK");
		}else {
			System.out.println("No");
		}
                applicationContext.close();
	}
}

2、查询

public class Test {

	public static void main(String[] args) {
		ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
		JdbcTemplate jdbcTemplate = applicationContext.getBean(JdbcTemplate.class);
		String sql = "select name from user_inf where id=?";
		String name = jdbcTemplate.queryForObject(sql, String.class, "1e549aeb-1417-4129-b94f-5f5089af0367");
		System.out.println(name);
                applicationContext.close();
	}
}

3、查询自定义类对象(内部类,实现RowMapper接口)

public class Test {

	public static void main(String[] args) {
		ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
		JdbcTemplate jdbcTemplate = applicationContext.getBean(JdbcTemplate.class);
		String sql  = "select * from user_inf where id=?";
		
		class UserInfoRowMapper implements RowMapper<UserInfo>{//   有名内部类

			public UserInfo mapRow(ResultSet rs, int arg1) throws SQLException {
				UserInfo userInfo = new UserInfo();
				userInfo.setId(rs.getString("id"));
				userInfo.setName(rs.getString("name"));
				userInfo.setPassword(rs.getString("password"));
				userInfo.setAddress(rs.getString("address"));
				return userInfo;
			}
		}
		UserInfo userInfo = jdbcTemplate.queryForObject(sql, new UserInfoRowMapper(), "1e549aeb-1417-4129-b94f-5f5089af0367");
                applicationContext.close();
	}
}

4、查询自定义类对象(内部类,实现ResultSetExtractor接口)

public class Test {

	public static void main(String[] args) {
		ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
		JdbcTemplate jdbcTemplate = applicationContext.getBean(JdbcTemplate.class);
		class UserInfoResult implements ResultSetExtractor<UserInfo>{

			@Override
			public UserInfo extractData(ResultSet rs) throws SQLException, DataAccessException {
				if(rs.next()) {//该接口需要判断是否有next
					UserInfo userInfo =new UserInfo();
					userInfo.setId(rs.getString("id"));
					userInfo.setName(rs.getString("name"));
					userInfo.setPassword(rs.getString("password"));
					userInfo.setAddress(rs.getString("address"));
					return userInfo;
				}
				return null;
			}
		}
		UserInfo userInfo = jdbcTemplate.query(sql, new UserInfoResult(), "1e549aeb-1417-4129-b94f-5f5089af0367");
		System.out.println(userInfo);
                applicationContext.close();
	}
}

5、查询自定义类对象(匿名内部类,实现RowMapper接口)

public class Test {

	public static void main(String[] args) {
		ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
		JdbcTemplate jdbcTemplate = applicationContext.getBean(JdbcTemplate.class);
		String sql  = "select * from user_inf where id=?";
		
		UserInfo userInfo = jdbcTemplate.queryForObject(sql, new RowMapper<UserInfo>(){//  匿名内部类

			public UserInfo mapRow(ResultSet rs, int arg1) throws SQLException {
				UserInfo userInfo = new UserInfo();
				userInfo.setId(rs.getString("id"));
				userInfo.setName(rs.getString("name"));
				userInfo.setPassword(rs.getString("password"));
				userInfo.setAddress(rs.getString("address"));
				return userInfo;
			}
		}, "1e549aeb-1417-4129-b94f-5f5089af0367");
                applicationContext.close();
	}
}

6、查询自定义类对象(lmd表达式,实现RowMapper接口)

public class Test {

	public static void main(String[] args) {
		ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
		JdbcTemplate jdbcTemplate = applicationContext.getBean(JdbcTemplate.class);
		String sql  = "select * from user_inf where id=?";
		
		UserInfo userInfo =  new UserInfo();
		jdbcTemplate.queryForObject(sql, (ResultSet rs, int arg1)-> {// lmd表达式(jdk 1.8版本支持)
				userInfo.setId(rs.getString("id"));
				userInfo.setName(rs.getString("name"));
				userInfo.setPassword(rs.getString("password"));
				userInfo.setAddress(rs.getString("address"));
				return userInfo;
		}, "1e549aeb-1417-4129-b94f-5f5089af0367");
		System.out.println(userInfo);
                applicationContext.close();
	}
}

7、获取数据库多条数据(使用query方法)

public class Test {

	public static void main(String[] args) {
		ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
		JdbcTemplate jdbcTemplate = applicationContext.getBean(JdbcTemplate.class);
		String sql="select * from user_inf";
		List<UserInfo> list = jdbcTemplate.query(sql, new RowMapper<UserInfo>() {

			@Override
			public UserInfo mapRow(ResultSet rs, int arg1) throws SQLException {
				UserInfo userInfo =new UserInfo();
				userInfo.setId(rs.getString("id"));
				userInfo.setName(rs.getString("name"));
				userInfo.setPassword(rs.getString("password"));
				userInfo.setAddress(rs.getString("address"));
				return userInfo;
			}
		});
                applicationContext.close();
	}
}

8、获取数据库多条数据(使用queryForObject方法)

public class Test {

	public static void main(String[] args) {
		ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
		JdbcTemplate jdbcTemplate = applicationContext.getBean(JdbcTemplate.class);
		String sql="select * from user_inf";
		List<UserInfo> list = jdbcTemplate.query(sql, new ResultSetExtractor<List<UserInfo>>() {

			@Override
			public List<UserInfo> extractData(ResultSet rs) throws SQLException, DataAccessException {
				
				List<UserInfo> list = new ArrayList<UserInfo>();
				while(rs.next()) {
					UserInfo userInfo =new UserInfo();
					userInfo.setId(rs.getString("id"));
					userInfo.setName(rs.getString("name"));
					userInfo.setPassword(rs.getString("password"));
					userInfo.setAddress(rs.getString("address"));
					list.add(userInfo);
				}
				return list;
			}
		});
		for (UserInfo ui : list) {
			System.out.println(ui.getName());
		}
                applicationContext.close();
	}
}