JDBC2.0之后提供了使用连接池(connection pooling)和数据源(DataSource)技术访问数据库,使用数据源的好处是不用为每个HTTP请求创建一个连接对象,Servlet建立数据库连接、执行查询、处理结果集、请求结束关闭连接。建立连接比较费时,如果每次请求都需要建立连接,将增大请求的响应时间。而数据源事先建立若干连接对象,将之存放在数据库连接池中供数据访问组件共享;由数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,并非重新建立一个新的连接,这样,避免了为每个HTTP请求建立连接,大大降低请求的响应时间。因此在实际项目应用中常常使用数据源技术访问数据库。
数据源是通过javax.sql.DataSource接口对象实现的,可以通过它的getConnection方法获得数据库连接,代替了之前JDBC使用DriverManager(驱动管理器类)工具获得数据库连接。
一、在项目中配置数据源
Tomcat配置数据源分:局部数据源和全局数据源,区别只是在于是否可被所有的应用程序使用。
1、为Tomcat添加JDBC驱动程序
JDBC驱动程序能够为JAVA程序与数据库系统建立通信渠道。将下载好的JDBC驱动程序包放置在tomcat根目录下的lib文件夹下:
本项目数据库采用mysql,下载链接,菜鸟教程中有:https://www.runoob.com/java/java-mysql-connect.html
重启tomcat,之后在web应用程序根目录下的META-INF创建配置文件context.xml(一定是context.xml!)
context.xml内容如下:
之后需要在web.xml中继续配置一下
MyExam
index.html
index.htm
index.jsp
default.html
default.htm
default.jsp
MySQL DBCP
myDataSource
javax.sql.DataSource
Container
二、在项目中使用数据源
在项目中使用传输对象(通常是JavaBeans)实现对象与数据库中关系数据的映射(orm,通俗点讲就是一张表对应一个类,关键字作为类的私有属性,,类中提供属性的get、set方法)。传输对象只包含数据元素,不涉及业务逻辑,业务逻辑由业务对象实现(通常就是设计对应的DAO类,里面实现关于该表某属性的增删改查操作,提供方法给servlet进行操作)。传输对象通常用于将数据从表示层传输到业务层,或者从业务层传输到表示层,它必须是可序列化的,即必须实现java.io.Serializable接口。
首先定义一个User类作为传输对象,用于保存或读取数据。User.java:
package com.myexam.entity;
public class User {
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
对应数据库如下:
接下来设计对应UserDao.java进行增删改查,每次对数据库进行操作都要连接数据库,连接数据库的这段代码是通用的,我们可以将之抽取出来作为一个基础类,之后的其他Dao可通过该基础类派生出来。所以先设计基础类BaseDao.java,在BaseDao.java中获取数据源对象,再通过数据源获得连接对象。
通过数据源对象获得数据库连接对象不能直接通过实例化方法来生成DataSource对象,而是采用Java命名与目录接口(JNDI)技术获得DataSource对象的引用。根据课本,可以简单理解JNDI为一种将名字和对象绑定的技术,对象工厂负责创建对象,这些对象都和唯一的名字绑定,外部程序可以通过名字来获得某个对象的访问。关于JNDI的深入理解可以参考:https://blog.csdn.net/sunkobe2494/article/details/50824359
那么获取数据源需要使用JNDI技术,我们可以通过提供名字与对象绑定的上下文对象lookup(name)方法获得数据源,再通过数据源对象的getConnection()获得数据库连接对象Connection。
BaseDao.java:
package com.myexam.dao;
import java.sql.Connection;
import javax.sql.DataSource;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class BaseDao {
DataSource dataSource;
//在构造方法里获得数据源
public BaseDao(){
try{
Context context = new InitialContext();
//lookup的参数必须加上java:comp/env前缀+数据源名称
dataSource = (DataSource)context.lookup("java:comp/env/myDataSource");
}catch(NamingException ne){
System.out.println("NamingException:"+ne);
}
}
//获得数据库连接对象
public Connection getConnection() throws Exception{
return dataSource.getConnection();
}
}
如果出现getConnection方法报错如下,注意DataSource导入包是否是:import javax.sql.DataSource;以及Connection导入的包是否是:import java.sql.Connection;
创建UserDao类,继承BaseDao类,在UserDao类中可以实现数据库的增删改查操作。这就免不了要写数据库操作语句,这里使用的是PreparedStatement不是Statement,它们的区别就是,PreparedStatement是预编译好了的sql语句,在需要写入外部传入的数据的地方,使用占位符?,当调用该方法传入外部数据时,数据被填入已经编译好的sql语句中执行。这样就当一个sql语句被多次执行时,数据库就不用每执行一次就编译一次,做很多重复的操作,有利于提高执行效率。
String sql = "INSERT INTO user"+"(username,password)VALUES(?,?)";
之后对sql语句进行预编译之后获得PreparedStatement对象,再将外部数据与占位符进行绑定
Connection conn = dataSource.getConnection();
//预编译处理
PreparedStatement pstmt = conn.prepareStatement(sql);//绑定数据,1代表第一个占位符
pstmt.setString(1, user.getUsername());pstmt.setString(2, user.getPassword());
//执行sql语句
pstmt.executeUpdate();
UserDao.java:
package com.myexam.dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import com.myexam.entity.User;
public class UserDao extends BaseDao{
public boolean addUser(User user) {
String sql = "INSERT INTO user"+"(username,password)VALUES(?,?)";
try {
Connection conn = dataSource.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, user.getUsername());
pstmt.setString(2, user.getPassword());
pstmt.executeUpdate();
pstmt.close();
conn.close();
return true;
} catch (SQLException e) {
e.printStackTrace();
return false;
}
}
}
如果出现Connection的prepareStatement方法报错如下,注意PreparedStatement导入包是否是:import java.sql.PreparedStatement;
至此,在用户进行注册请求的时候,创建registerServlet类去处理注册请求,可以在registerServlet类中使用UserDao类中定义的方法将数据插入数据库
registerServlet.java:
package com.myexam.servlet.register;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.myexam.dao.UserDao;
import com.myexam.entity.User;
/**
* Servlet implementation class RegisterServlet
*/
@WebServlet("/register.do")
public class RegisterServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public RegisterServlet() {
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
response.getWriter().append("Served at: ").append(request.getContextPath());
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
User user = new User();
UserDao dao = new UserDao();
//如果用户名出现乱码,可能需要在从处用utf-8重新编码一下,String(str,"utf-8");
user.setUsername(request.getParameter("username"));
user.setPassword(request.getParameter("password"));
boolean success = dao.addUser(user);
if(success){
System.out.println("注册成功");
}else{
System.out.println("注册失败");
}
}
}
之后只需要创建个register.jsp页面进行请求 。
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
注册