資料庫連線池&Spring JDBC(JdbcTemplate)
“當你越來越優秀的時候就會遇見越來越好的人。”你好,我是夢陽辰,和我一起加油吧!
【問題提出】
之前我們連線資料庫,都會執行一次建立和斷開Connection物件的操作,這樣頻繁的操作十分影響資料庫的訪問效率,並且增加了程式碼量。
這就好比,你開一個飯店,當客人來了,你招一個服務員為客人提供服務,當客人走了,你就把服務員開了。
文章目錄
01.為什麼要資料庫連線池
為了避免頻繁的建立資料庫連線,資料庫連線池應運而生。資料庫連線池負責分配、管理和釋放資料庫連線。它允許應用程式重複使用現有的資料庫連線,而不是重新建立。
資料庫連線池在初始化的時候建立一定數量的資料庫連線放到資料庫連線池中,當應用程式訪問時向連線池申請一個Connection。如果有空閒的Connection,則將其返回,否則建立新的Connection。使用完畢後,連線池會將該Connection收回,交付給其它執行緒使用,以減少建立資料庫連線的次數,提高資料庫訪問的效率。
資料庫連線池
概念:其實就是一個容器(集合),存放資料庫連線的容器。
特點:節約了資源,使用者訪問高效。
02.資料庫連線池的實現
標準介面:DataSource介面
在javax.sql包下。
為了獲取資料庫連線物件,JDBC提供了javax.sql。DataSource介面,他負責與資料庫建立連線,並定義了返回值為Connection物件的方法。
1.方法:
獲取連線:
Connection getConnection()
Connection getConnection(String username, String password)
歸還連線:如果連線物件Connection時是從連線池中獲取的,
那麼呼叫Connection.close()方法,則不會再關閉連線了。
而是歸還連線
2.一般我們不去實現它,由資料庫廠商來實現。
通常把實現了DataSource介面的類稱為資料來源,資料來源儲存了所有建立資料庫連線的資訊。資料來源中包含資料庫連線池。
常用的
03.C3P0:資料庫連線池技術
步驟:
1.匯入jar包:c3p0-0.9.5.2.jar mchange-commons-java-0.2.12.jar
2.定義配置檔案(自動載入)
名稱:c3p0.properties 或者 c3p0-config.xml
路徑:直接將檔案放在src目錄下。
3.建立核心物件,資料庫連線池物件 ComboPooledDataSource
DataSource ds = new ComboPooledDataSource();
4.獲取資料庫連線物件
Connection conn = ds.getConnection();
5.剩下操作跟基本操作相同。
@WebServlet("/C3p0ServletTest1")
public class C3p0ServletTest1 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
//1.建立資料庫連線池物件
DataSource ds = new ComboPooledDataSource();
Connection conn = null;
PreparedStatement pstat = null;
ResultSet rs = null;
try {
//2.獲取連線物件
conn = ds.getConnection();
//3.定義sql
String sql = "select * from student where sex =?";
//4.獲取執行sql的物件
pstat = conn.prepareStatement(sql);
pstat.setString(1, "男");
//5.執行sql
rs = pstat.executeQuery();
while (rs.next()) {
String sequence = rs.getString("sequence");
String name = rs.getString("name");
String sex = rs.getString("sex");
out.println("<p>" + sequence + " " + name + " " + sex + "<p>");
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
try {
if(rs!=null){
rs.close();
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
if(pstat!=null){
try {
pstat.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws
ServletException, IOException {
doPost(request, response);
}
}
04.Druid:資料庫連線池實現技術
Druid由阿里巴巴提供。
步驟:
1.匯入jar包:druid-1.0.9.jar。
2.定義配置檔案:
是proporties形式。
名稱:可以交任意名稱,可以放在任意目錄下(需要手動載入)
3.獲取資料庫連線池物件:通過工廠類來獲取
DruidDataSourceFactory
4.獲取連線
5.其他的步驟相同
@WebServlet("/DruidServletTest1")
public class DruidServletTest1 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
DataSource ds = null;
Connection conn = null;
PreparedStatement pstat = null;
//1.載入配置檔案
Properties pro = new Properties();
InputStream is = DruidServletTest1.class.getClassLoader().getResourceAsStream("druid.properties");
pro.load(is);
//2.獲取連線池物件
try {
ds = DruidDataSourceFactory.createDataSource(pro);
} catch (Exception e) {
e.printStackTrace();
}
try {
//5.獲取連線
conn = ds.getConnection();
//3.定義sql
String sql = "update student set name=? where birthday=?";
//4.獲取執行sql的物件
pstat = conn.prepareStatement(sql);
pstat.setString(1, "夢陽辰");
pstat.setString(2,"1999-10-18");
//5.執行sql
int count = pstat.executeUpdate();
if(count>0){
out.print("修改成功!");
}else{
out.print("修改失敗!");
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
if(pstat!=null){
try {
pstat.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request,response);
}
}
2.定義工具類,簡化開發
定義一個JDBCUtils類。
提供方法:
1.獲取連線的方法:通過資料庫連線池獲取。
2.釋放資源
3.獲取連線池的方法。
/**
* Druid連線池的工具類
*/
public class JdbcUtils {
//1.定義成員變數DateaSource
private static DataSource ds;
static {
try{
//1.載入配置檔案
Properties pro = new Properties();
InputStream is = JdbcUtils.class.getClassLoader().getResourceAsStream("druid.properties");
pro.load(is);
//2.獲取DataSource
ds = DruidDataSourceFactory.createDataSource(pro);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 獲取連線
*/
public static Connection getConntion() throws SQLException {
return ds.getConnection();
}
/**
* 釋放資源
*/
public static void close(Statement stmt,Connection conn){
/* if(stmt!=null){
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn!=null){//歸還連線
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}*/
close(null,stmt,conn);
}
/**
* 釋放資源
*/
public static void close(ResultSet rs, Statement stmt, Connection conn){
if(rs!=null){
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(stmt!=null){
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn!=null){//歸還連線
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
/**
* 獲取連線池
*/
public static DataSource getDataSource(){
return ds;
}
}
05.Spring JDBC
JDBC已經能夠滿足大部分使用者最基本的需求,但是在使用JDBC時,必須自己來管理資料庫資源如:獲取 PreparedStatement,設定SQL語句引數,關閉連線等步驟。JdbcTemplate就是Spring對JDBC的封裝,目的是使 JDBC更加易於使用。JdbcTemplate是Spring的一部分。 JdbcTemplate處理了資源的建立和釋放。他幫助我們避免一些常見的錯誤,比如忘了總要關閉連線。他執行核心的JDBC工作流,如Statement的建立和執行,而我們只需 要提供SQL語句和提取結果。
JDBC的工具類,目的是為了簡化開發步驟。
Spring框架對JDBC的簡單框架,提供了一個JdbcTemplate物件簡化JDBC的開發。
步驟:
1.匯入jar包。
2.建立JdbcTemplate物件,依賴於資料來源DataSource。
JdbcTemplate template = new JdbcTemplate(ds);
3.呼叫JdbcTemplate的方法來完成CRUD的操作。
update():執行DML語句(insert,update,delete)。
queryForMap():查詢結果將結果封裝為map集合。結果只能為1條,
列名為key,值為value
queryForList():查詢結果將結果集封裝為list集合。
先將資料封裝為一個Map集合,再將Map集合轉載到List集合中
query():查詢結果,將結果封裝為JavaBean物件。(重點)
一般我們使用BeanPropertyRowMapper實現類。
可以完成到JavaBean的自動封裝。
new BeanPropertyRowMapper<型別>(型別.class)
queryForObject:查詢結果,將結果封裝為物件。
一般用於聚合函式的查詢
update()練習:
@WebServlet("/jdbcTemplateServletTest1")
public class jdbcTemplateServletTest1 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
//1.匯入jar包
//2.建立JdbcTemplate物件
JdbcTemplate template = new JdbcTemplate(JdbcUtils.getDataSource());
//定義sql
String sql = "insert into student(sequence,sex,name,birthday) values(?,?,?,?)";
//呼叫方法
int count = template.update(sql,"101000","男","張三","2020-11-19");
out.print(count);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request,response);
}
}
queryForMap()練習:
@WebServlet("/jdbcTemplateServletTestl2")
public class jdbcTemplateServletTestl2 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
JdbcTemplate template = new JdbcTemplate(JdbcUtils.getDataSource());
String sql = "select * from student where id = ?";
//只能封裝1條
Map<String,Object> map =template.queryForMap(sql,1);
out.print(map);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request,response);
}
}
queryForList()練習:
@WebServlet("/jdbcTemplateServletTestl2")
public class jdbcTemplateServletTestl2 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
JdbcTemplate template = new JdbcTemplate(JdbcUtils.getDataSource());
String sql = "select * from student where id > ?";
List<Map<String,Object>> list =template.queryForList(sql,1);
for(Map<String,Object> StringObjectMap:list){
out.print("<p>"+StringObjectMap+"</p>");
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request,response);
}
}
將Map轉換成JSON格式:
String sql="select * from student where id > ?";
List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql,1);
Map<String, Object> map = maps.get(2);
JSONObject jsonObject=new JSONObject(map);
System.out.println(jsonObject.toJSONString());
query練習:
@WebServlet("/jdbcTemplateServletTest2")
public class jdbcTemplateServletTest2 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
JdbcTemplate template = new JdbcTemplate(JdbcUtils.getDataSource());
String sql = "select * from student ";
List<Student> list = template.query(sql, new RowMapper<Student>() {
@Override
public Student mapRow(ResultSet rs, int i) throws SQLException {
Student student = new Student();
int id = rs.getInt("id");
String sequence = rs.getString("sequence");
String name = rs.getString("name");
String sex = rs.getString("sex");
Date birthday = rs.getDate("birthday");
student.setId(id);
student.setSequence(sequence);
student.setName(name);
student.setSex(sex);
student.setBirthday(birthday);
return student;
}
});
for(Student student :list){
out.print(student);
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request,response);
}
}
存在的問題:雖然我們用了Spring JDBC類,通過query練習但是我們並沒有實現簡化程式碼,跟以前差不多,那是因為我們自己去實現了 RowMapper介面, 其實JdbcTemplate已經幫我們實現了,我們直接使用就好了。
改進後:
@WebServlet("/JdbcTemplateServletTest3")
public class JdbcTemplateServletTest3 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
JdbcTemplate template = new JdbcTemplate(JdbcUtils.getDataSource());
String sql = "select * from student ";
List<Student> list = template.query(sql,new BeanPropertyRowMapper<Student>(Student.class));
for(Student student:list){
out.print(list);
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request,response);
}
}
@WebServlet("/JdbcTemplateServletTest3")
public class JdbcTemplateServletTest3 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
JdbcTemplate template = new JdbcTemplate(JdbcUtils.getDataSource());
String sql = "select * from student where id =?";
//將查詢結果封裝成了java物件
List<Student> list = template.query(sql,new BeanPropertyRowMapper<Student>(Student.class),"1");
out.print(list.get(0).getName());
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request,response);
}
}
注意JavaBean類,如果屬性定義為基本型別,在查詢資料庫時,如果欄位為空,會報錯,可以將JavaBean屬性基本型別改為引用資料型別。
queryForObject練習:
@WebServlet("/JdbcTemplateServletTest3")
public class JdbcTemplateServletTest3 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
JdbcTemplate template = new JdbcTemplate(JdbcUtils.getDataSource());
String sql ="select count(id) from student";
Long total = template.queryForObject(sql,Long.class);
out.print(total);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request,response);
}
}
【提出問題】
如果這裡不使用自定義工具類JdbcUtils得到連線池,而是直接獲取連線池,多次訪問後,則會出現too many connection情況。
JdbcTemplate template = new
JdbcTemplate(JdbcUtils.getDataSource());
06.登入案例
登入頁面:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="../../case43/res/js/jquery.min.js"></script>
<style type="text/css">
p{
color:red;
}
</style>
</head>
<body>
<form class="fm">
<div>
<label>賬號:</label>
<input type="text" name="userName" id="userName">
</div>
<div>
<label>密碼:</label>
<input type="password" name="passWord" id="passWord">
</div>
<input type="button" id="btn" value="確認">
</form>
<p id="p"></p>
<script>
$(function(){
$("#btn").click(function(){
var dataFm = $(".fm").serialize();
$.post(
"/teaching/com/teaching/homeWork/Check1",
dataFm,
function(data){
var json =JSON.parse(data);
if(json.success){
window.location.href= json.url;
}else{
$("#p").html("賬號或密碼錯誤!");
}
}
);
});
});
</script>
</body>
</html>
工具類:
/**
* Druid連線池的工具類
*/
public class JdbcUtils {
//1.定義成員變數DateaSource
private static DataSource ds;
static {
try{
//1.載入配置檔案
Properties pro = new Properties();
InputStream is =JdbcUtils.class.getClassLoader().getResourceAsStream("druid.properties");
pro.load(is);
//2.獲取DataSource
ds = DruidDataSourceFactory.createDataSource(pro);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 獲取連線
*/
public static Connection getConntion() throws SQLException {
return ds.getConnection();
}
/**
* 釋放資源
*/
public static void close(Statement stmt,Connection conn){
close(null,stmt,conn);
}
/**
* 釋放資源
*/
public static void close(ResultSet rs, Statement stmt, Connection conn){
if(rs!=null){
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(stmt!=null){
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn!=null){//歸還連線
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
/**
* 獲取連線池
*/
public static DataSource getDataSource(){
return ds;
}
}
處理request:
@WebServlet("/com/teaching/homeWork/Check1")
public class Check1 extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public Check1() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// TODO Auto-generated method stub
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
String userName = request.getParameter("userName");
String passWord = request.getParameter("passWord");
JdbcTemplate template = new JdbcTemplate(JdbcUtils.getDataSource());
//因為teaching表沒有賬號和密碼欄位,為了減少時間,預設將name作為賬號,sequence作為密碼
String sql = "select * from student where name = ? and sequence = ?";
//將查詢結果封裝成了java物件,如果沒有執行成功會報異常(則會存在問題)
Student student = null;
try {
student =template.queryForObject(sql, new BeanPropertyRowMapper<Student>(Student.class),userName,passWord);
} catch (EmptyResultDataAccessException e) {
e.printStackTrace();
}
//Student student = template.queryForObject(sql, new BeanPropertyRowMapper<Student>(Student.class),userName,passWord);
if(student!=null&&userName.equals(student.getName())&&passWord.equals(student.getSequence())) {
HttpSession session = request.getSession();
session.setAttribute("userName",student.getName());
session.setAttribute("passWord",student.getSequence());//密碼
//設定session存活時間
Cookie id = new Cookie("JSESSIONID",session.getId());
id.setMaxAge(60*60*24*10);//10天
response.addCookie(id); //轉發
String url= request.getContextPath()+"/case631/homeWork/html/success.jsp";
request.setAttribute("success",true);
request.setAttribute("url",url);
request.setAttribute("student", student);
String url1 ="/com/teaching/homeWork/Json";
RequestDispatcher rd =request.getRequestDispatcher(url1);
rd