目录
Servlet对象的工作原理及其生命周期、service()方法
[Servlet传递参数到JSP ---使用session](#Servlet传递参数到JSP ---使用session)
[Servlet传递参数到JSP ---使用request](#Servlet传递参数到JSP ---使用request)
Servlet
Servlet简介
- Servlet是使用Java语言编写的服务器端程序,可以像JSP一样生成动态的WEB页。
- Servlet主要运行在服务器端,并由服务器调用执行,是一种按照Servlet标准开发的类。
- Servlet采用了多线程的处理方式,并保留了Java的可移植性特点,使得Servlet更易使用且功能更加强大。
什么是Servlet?
Servlet是一个Java编程语言中的接口,主要用于扩展服务器的功能。Servlet可以用来处理HTTP请求,并返回响应给客户端。它们常用于构建动态Web应用程序。
java
import javax.servlet.*;
import java.io.*;
public class HelloWorldServlet extends GenericServlet {
public void service(ServletRequest request, ServletResponse response)
throws ServletException, IOException {
// 设置响应内容类型为文本/html
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<body>");
out.println("<h1>Hello, World!</h1>");
out.println("</body>");
out.println("</html>");
}
}
上面的代码展示了一个简单的HelloWorldServlet,它继承自GenericServlet,并实现了service方法。在这个方法里,我们设置了响应的内容类型为
text/html
,然后通过getWriter()
获取到一个PrintWriter对象,最后向客户端发送一个简单的HTML字符串。
Servlet生命周期
Servlet的生命周期包括加载、初始化、服务、销毁四个阶段。
- 加载:当服务器启动或者第一次接收到对某个Servlet的请求时,会加载该Servlet。
- 初始化:加载完成后,服务器会调用Servlet的
init()
方法进行初始化操作。 - 服务:当服务器接收到客户端的请求后,会调用Servlet的
service()
方法处理请求并产生响应。 - 销毁:当服务器关闭或者Servlet不再使用时,服务器会调用Servlet的
destroy()
方法释放资源。
多线程处理
由于Servlet是在服务器端运行的,所以它可以同时处理多个客户端的请求。这意味着Servlet必须能够处理并发情况,因此Servlet默认就是线程安全的。
java
import javax.servlet.*;
import java.util.concurrent.atomic.AtomicInteger;
public class CounterServlet extends HttpServlet {
private AtomicInteger counter = new AtomicInteger(0);
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
int currentCount = counter.incrementAndGet();
resp.getWriter().println("Current count is " + currentCount);
}
}
Servlet处理的基本流程
- 客户端(通常是Web浏览器)通过HTTP协议提出请求。
- Web服务器接收该请求并将它转发给相应的Servlet。
- 如果Servlet尚未加载,Web服务器将其加载到Java虚拟机并执行。
- Servlet接收HTTP请求并执行某些处理。
- Servlet生成响应内容并将其传回给服务器。
- 服务器将从Servlet收到的响应返回给客户端。
java
import javax.servlet.*;
import java.io.*;
public class HelloServlet extends GenericServlet {
@Override
public void service(ServletRequest request, ServletResponse response)
throws ServletException, IOException {
// 设置响应内容类型为文本/html
response.setContentType("text/html");
// 获取输出流
PrintWriter out = response.getWriter();
// 输出响应内容
out.println("<html>");
out.println("<body>");
out.println("<h1>Hello, Servlet</h1>");
out.println("</body>");
out.println("</html>");
}
}
在这个例子中,我们创建了一个名为
HelloServlet
的Servlet,它继承自GenericServlet
。当服务器接收到请求并转发给这个Servlet时,它会调用service()
方法。在这个方法内,我们设置了响应的内容类型为text/html
,然后通过getWriter()
获取到一个PrintWriter
对象,最后向客户端发送一个简单的HTML字符串。
为了使这个Servlet能够在服务器上运行,我们需要在Web应用的部署描述符(通常为web.xml
)中配置它
java
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.example.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
这段XML配置告诉服务器,当接收到以
/hello
结尾的URL请求时,应该调用HelloServlet
来处理。现在,如果你在浏览器中输入
http://yourserver.com/hello
,服务器就会加载并执行HelloServlet
,然后将结果返回给你。这就是Servlet处理请求的基本流程
Servlet程序实现
在整个Servlet程序之中最重要的就是Servlet接口,在此接口下定义了一个GenericServlet的子类,但是一般不会直接继承此类,而是根据所使用的协议选择GenericServlet的子类继承,例如:现在是采用HTTP协议处理的,所以一般而言当需要使用HTTP协议操作时用户自定义的Servlet类都要继承HttpServlet类。
Servlet
接口:这是所有Servlet的基础,定义了Servlet的核心行为。GenericServlet
:这是一个抽象类,实现了Servlet
接口,提供了通用的Servlet功能,但一般不直接使用。HttpServlet
:这是GenericServlet
的一个子类,专门针对HTTP协议进行了优化,大部分情况下,用户自定义的Servlet都会继承此类。- 用户自定义Servlet:在此基础上,我们可以根据需求创建自己的Servlet类。
如何实现一个简单的Servlet:
- 编写一个Servlet对象的类意味着要编写一个特殊的类,这个特殊类是
javax.servlet.http.HttpServlet
的子类。 HttpServlet
类实现了Servlet
接口,包含了响应用户请求的方法。HttpServlet
的子类被称为一个Servlet类。
首先,你需要导入必要的包:
java
import javax.servlet.*;
import java.io.*;
然后,你可以创建一个继承自HttpServlet
的类,比如MyServlet
java
public class MyServlet extends HttpServlet {
...
}
接下来,你可能想要覆盖doGet
或doPost
方法,因为它们分别对应HTTP的GET和POST请求
java
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 这里处理GET请求
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 这里处理POST请求
}
在这些方法中,你可以读取请求数据,处理业务逻辑,然后生成响应。例如,你可以这样处理GET请求:
java
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 设置响应内容类型为文本/html
response.setContentType("text/html");
// 获取输出流
PrintWriter out = response.getWriter();
// 输出响应内容
out.println("<html>");
out.println("<body>");
out.println("<h1>Hello, Servlet</h1>");
out.println("</body>");
out.println("</html>");
}
注意,
HttpServletRequest
和HttpServletResponse
都是Servlet
接口的实现类,它们提供了处理HTTP请求和响应的方法。
为了让服务器知道你的Servlet,你需要在web.xml
文件中配置它
java
<servlet>
<servlet-name>myServlet</servlet-name>
<servlet-class>com.example.MyServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>myServlet</servlet-name>
<url-pattern>/myservlet</url-pattern>
</servlet-mapping>
这段配置表示,当你访问
http://yourserver.com/myservlet
时,服务器会调用MyServlet
。
总结一下,Servlet程序实现的基本步骤如下:
- 创建一个继承自
HttpServlet
的类。 - 覆盖
doGet
或doPost
方法,处理HTTP请求。 - 在
web.xml
中配置Servlet,指定其名称和映射路径。
Servlet对象的工作原理及其生命周期、service()
方法
工作原理:
- Servlet对象由Tomcat服务器负责管理,Tomcat服务器通过读取
web.xml
来创建并运行Servlet对象。 - Servlet对象有三个重要的生命周期方法:
init()
,service()
和destroy()
。
生命周期:
- 初始化:当一个Servlet被实例化之后,容器将调用
init()
方法初始化这个对象,初始化是为了让Servlet对象在处理客户端请求前完成一些初始化的工作,例如建立数据库连接、读取资源文件信息等。如果初始化失败,则此Servlet将被直接卸载。 - 处理服务:当有请求提交时,Servlet将调用
service()
方法(doGet()
或doPost()
)进行处理,在service()
方法中,Servlet可以通过ServletRequest
接收客户的请求,也可以利用ServletResponse
设置响应信息。 - 销毁:当WEB容器关闭或者检测到一个Servlet要从容器中被删除时,会自动调用**destroy()**方法,以便让该实例释放掉所占用的资源。
- 卸载:当一个Servlet调用完destroy()方法后,此实例将等待被垃圾收集器所回收,当要再次使用此Servlet的时候,会重新调用init()方法初始化
service()
方法
service()
方法是一个公共的void方法,接受HttpServletRequest
和HttpServletResponse
作为参数,抛出IOException
和ServletException
异常。- 当Servlet对象创建和初始化后,该对象就调用
service()
方法来处理用户的请求并返回响应。 - 不同的客户请求该Servlet对象时,服务器将启动一个新的线程,在该新线程中调用
service()
方法响应客户的需求。即每个客户的每次请求都导致service()
方法被执行,调用过程在不同的线程中,互不干扰。
首先,你需要导入必要的包
java
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
然后,你可以创建一个继承自HttpServlet
的类,比如MyServlet
java
public class MyServlet extends HttpServlet {
...
}
为了实现Servlet的生命周期方法,你需要重载init()
, service()
和 destroy()
方法:
java
@Override
public void init(ServletConfig config) throws ServletException {
System.out.println("Servlet初始化");
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
System.out.println("服务客户请求");
}
@Override
public void destroy() {
System.out.println("Servlet销毁");
}
注意,
init()
方法用于完成一些初始化工作,如加载资源、设置属性等;service()
方法用于处理客户端请求;destroy()
方法用于释放资源。
为了让服务器知道你的Servlet,你需要在web.xml
中配置它
java
<servlet>
<servlet-name>myServlet</servlet-name>
<servlet-class>com.example.MyServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>myServlet</servlet-name>
<url-pattern>/myservlet</url-pattern>
</servlet-mapping>
这段配置表示,当你访问
http://yourserver.com/myservlet
时,服务器会调用MyServlet
。
总结一下
Servlet对象的工作原理及生命周期如下:
- 当服务器启动时,会根据
web.xml
中的配置信息创建Servlet实例,并调用init()
方法进行初始化。 - 当客户端发送请求时,服务器会调用
service()
方法处理请求。 - 当服务器关闭时,会调用
destroy()
方法释放资源
service()
方法的主要作用如下:
- 当服务器接收到客户端的请求时,会启动一个新的线程来执行
service()
方法。 service()
方法可以根据请求类型调用doGet()
或doPost()
等方法来处理特定类型的请求。- 每个客户的每次请求都会导致
service()
方法被单独执行,因此不会互相干扰。
Servlet对象的共享变量
Servlet类是HttpServlet
的一个子类,在编写子类时可以声明一些成员变量。当用户请求加载Servlet时,服务器分别为每个用户启动一个线程,在该线程中Servlet调用service()
方法响应客户的需求,而Servlet类的成员变量是被所有线程共享的数据。
java
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
public class MyServlet extends HttpServlet {
private int count = 0;
...
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
synchronized (this) {
count++;
System.out.println("当前访问次数:" + count);
}
}
我们声明了一个名为
count
的成员变量,它的初始值为0每当有一个新的请求到来时,
count
就会加1,并打印出来
通过JSP页面访问****servlet
可以通过JSP页面来请求一个Servlet。也就是说,可以让JSP页面负责数据的显示,而让一个Servlet去做和处理数据有关的事情。
Web服务目录下的JSP页面都可以通过表单或超链接请求该Web服务目录下的某个Servlet。
eg. 创建一个JSP页面,比如index.jsp
java
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>访问Servlet示例</title>
</head>
<body>
<form action="MyServlet" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="submit" value="登录">
</form>
</body>
</html>
在这个例子中,我们创建了一个简单的表单,用户输入用户名和密码后点击"登录"按钮,表单将会提交到
MyServlet
。
创建一个Servlet,比如MyServlet.java
java
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
public class MyServlet extends HttpServlet {
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
if ("admin".equals(username) && "123456".equals(password)) {
response.sendRedirect("/success.jsp");
} else {
response.sendRedirect("/error.jsp");
}
}
}
在这个例子中,我们检查用户名和密码是否正确,如果正确则跳转到
/success.jsp
,否则跳转到/error.jsp
。
通过JSP页面访问Servlet的主要步骤如下:
- 创建一个JSP页面,包含一个表单或者超链接。
- 表单或超链接指向一个Servlet。
- 编写Servlet处理请求。
- 配置
web.xml
。
doGet()方法和doPost()方法
这两个方法都是Servlet类中的方法,它们是处理HTTP请求的主要入口点。在Servlet生命周期中,每当有新的HTTP请求到达Servlet容器时,Servlet引擎就会调用相应的doGet()
或者doPost()
方法来处理这个请求。
doGet()
方法用来处理HTTP的GET请求,而doPost()
方法则用来处理HTTP的POST请求。这两种请求方式的主要区别在于,GET请求的数据会被附加到URL后面,以问号的形式出现;而POST请求的数据则是放在HTTP包体中的。
java
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class MyServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 设置响应内容类型
response.setContentType("text/html");
// 获取输出流
PrintWriter out = response.getWriter();
// 输出欢迎信息
out.println("<html>");
out.println("<body>");
out.println("<h1>Welcome to Servlet</h1>");
out.println("</body>");
out.println("</html>");
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 这里可以处理POST请求特有的逻辑,比如上传文件等
// 然后我们可以调用doGet()方法来处理剩下的逻辑
this.doGet(request, response);
}
}
我们创建了一个名为
MyServlet
的Servlet类,并重写了doGet()
和doPost()
方法。在doGet()
方法中,我们简单地返回了一段HTML文本作为响应。而在doPost()
方法中,虽然没有具体的实现,但你可以在这里添加处理POST请求的特定逻辑,例如处理文件上传等。最后,我们调用了this.doGet(request, response)
,这样就可以共享doGet()
方法中的逻辑了。
Servlet跳转
从一个JSP或者是一个HTML页面可以通过表单或超链接跳转进Servlet,那么从Servlet也可以跳转到其他的Servlet、JSP或其他页面。
跳转的两种形式
1.客户端跳转(重定向):地址栏会跳转
客户端跳转,也称为重定向,是一种常见的Web应用程序中的跳转方式。它允许用户从一个Servlet或者JSP页面跳转到另一个Servlet或者JSP页面。这种跳转方式的特点是浏览器会发起一个新的HTTP请求,因此在新页面中无法访问原请求中的数据
在Servlet中,我们通常使用HttpServletResponse
接口的sendRedirect()
方法来进行客户端跳转。
java
import javax.servlet.http.*;
public class RedirectServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 设置响应头为重定向
response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
response.setHeader("Location", "http://www.example.com/newPage.jsp");
// 结束响应
response.flushBuffer();
}
}
当用户访问
RedirectServlet
时,Servlet将会把用户重定向到http://www.example.com/newPage.jsp
这个页面。需要注意的是,由于这是客户端跳转,所以新的页面无法直接访问原来请求中的数据。
另外,客户端跳转只能传递session范围内的属性,而不能传递request范围的属性。这是因为,在重定向过程中,浏览器会发起一个新的HTTP请求,原来的request对象已经失效了。如果需要在多个页面之间共享数据,建议使用session对象存储数据。
2.服务器端跳转(请求转发):地址栏不会跳转
服务器端跳转,也被称为请求转发,是一种在Java Web开发中用于在不同Servlet或JSP页面之间进行跳转的方法。
java
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;
@WebServlet("/FirstServlet")
public class FirstServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 获取RequestDispatcher对象
RequestDispatcher rd = request.getRequestDispatcher("SecondServlet");
// 转发请求
rd.forward(request, response);
}
}
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;
@WebServlet("/SecondServlet")
public class SecondServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 访问来自第一个Servlet的数据
String dataFromFirstServlet = (String) request.getParameter("data");
// 处理数据...
}
}
FirstServlet
首先获取了一个RequestDispatcher
对象,然后调用其forward()
方法将请求转发给了SecondServlet
。SecondServlet
可以在接收到的请求中访问FirstServlet
传来的数据。
服务器端跳转的优点在于它可以方便地在不同的Servlet或JSP页面之间共享数据,这对于需要跨页面处理数据的应用程序非常有用。然而,它的缺点是它要求所有的Servlet和JSP页面都在同一个Web应用中,而且必须位于同一台服务器上。
使用session
HTTP协议本身是一种无状态协议,也就是说服务器并不知道哪个请求来自于哪个用户。为了跟踪用户的状态信息,我们需要一种机制来记录每个用户的特定信息。这就是Session的作用。
Session是一种技术,用来保存用户的信息,例如登录状态、购物车等。每个用户都有自己的Session,它们之间互不干扰。Servlet类使用session对象来记录有关连接的信息。
获取用户会话的方法是通过HttpServletRequest
的对象request
调用getSession()
方法来获取用户的会话对象。
一个用户在不同的servlet对象中获取的session对象是完全相同的,不同的用户的session对象互不相同。
java
import javax.servlet.*;
import javax.servlet.http.*;
public class SessionServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 获取session对象
HttpSession session = request.getSession(true);
// 如果session不存在,则创建一个新的session
if(session == null){
session = request.getSession();
}
// 设置session的属性
session.setAttribute("username", "John Doe");
// 获取session的属性
String username = (String) session.getAttribute("username");
}
}
getSession(true)
方法会尝试获取当前用户的session。如果没有找到对应的session,就会自动创建一个新的session。然后我们可以使用setAttribute()
方法来设置session的属性,以及getAttribute()
方法来获取session的属性。
需要注意的是,一个用户在不同的servlet对象中获取的session对象是完全相同的,不同的用户的session对象互不相同。这意味着,你可以在一个servlet中设置session的属性,然后在另一个servlet中读取这些属性。
Servlet传递参数到JSP ---使用session
在Servlet中,你可以使用HttpSession
对象来传递参数到JSP页面。
java
import javax.servlet.*;
import javax.servlet.http.*;
public class ServletExample extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 获取session对象
HttpSession session = request.getSession();
// 设置session的属性
session.setAttribute("name", "John Doe");
// 使用客户端跳转
response.sendRedirect("jsp/example.jsp");
}
}
首先获取了session对象,然后设置了名为"name"的属性,并将其值设为"John Doe"。然后我们使用
sendRedirect()
方法进行了客户端跳转
在JSP页面中,我们可以使用EL表达式或者JSTL标签库来获取这个属性
java
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<body>
<h1>Hello ${sessionScope.name}!</h1> <!-- 使用EL表达式 -->
</body>
</html>
或者
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<body>
<h1>Hello <c:out value="${sessionScope.name}" />!</h1> <!-- 使用JSTL标签库 -->
</body>
</html>
当然,你也可以使用getAttribute()方法来获取这个属性
<%
String name = (String) session.getAttribute("name");
%>
<html>
<body>
<h1>Hello <%=name %>!</h1> <!-- 使用脚本元素 -->
</body>
</html>
需要注意的是,这种方法适用于客户端跳转和服务器端跳转。无论你是使用sendRedirect()
还是forward()
方法,只要用户还在同一个session内,就可以访问到这个属性。
Servlet传递参数到JSP ---使用request
在Servlet中,除了使用session之外,还可以使用request
对象来传递参数到JSP页面。这是一种更有效率的方法,因为它不需要创建新的session,只需要将请求转发给目标页面即可。
java
import javax.servlet.*;
import javax.servlet.http.*;
public class ServletExample extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 设置request的属性
request.setAttribute("name", "John Doe");
// 使用服务器端跳转
RequestDispatcher dispatcher = request.getRequestDispatcher("A.jsp");
dispatcher.forward(request, response);
}
}
首先设置了名为"name"的属性,并将其值设为"John Doe"。然后我们使用
forward()
方法进行了服务器端跳转。
在JSP页面中,我们可以使用EL表达式或者JSTL标签库来获取这个属性
java
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<body>
<h1>Hello ${name}!</h1> <!-- 使用EL表达式 -->
</body>
</html>
或者
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<body>
<h1>Hello <c:out value="${name}" />!</h1> <!-- 使用JSTL标签库 -->
</body>
</html>
或者
<%
String name = (String) request.getAttribute("name");
%>
<html>
<body>
<h1>Hello <%=name %>!</h1> <!-- 使用脚本元素 -->
</body>
</html>
注意,这种方法只适用于服务器端跳转。因为只有服务器端跳转才能保持原始的request对象,客户端跳转会创建一个新的request对象,所以无法访问到之前设置的属性。
JSP与数据库访问
JSP(JavaServer Pages)是一种动态网页技术,可以用来生成HTML、XML或其他类型的文档。在JSP中,可以通过JDBC(Java Database Connectivity)来访问数据库
java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class MySQLConnection {
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(
"jdbc:mysql://localhost:3306/mydatabase",
"username",
"password"
);
}
}
增删查改"数据库记录:
- 增加记录:使用
PreparedStatement
的executeUpdate()
方法执行INSERT语句。- 删除记录:使用
PreparedStatement
的executeUpdate()
方法执行DELETE语句。- 查询记录:使用
Statement
或PreparedStatement
的executeQuery()
方法执行SELECT语句。- 修改记录:使用
PreparedStatement
的executeUpdate()
方法执行UPDATE语句。
一个简单的例子,展示了如何使用JDBC从MySQL数据库中查询数据
java
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class JDBCDemo {
public static void main(String[] args) {
try {
Connection conn = MySQLConnection.getConnection();
String sql = "SELECT * FROM users WHERE id = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, 1); // 设置占位符的值
ResultSet rs = pstmt.executeQuery(); // 执行SQL语句
while(rs.next()) { // 遍历结果集
System.out.println(rs.getString("name"));
}
rs.close();
pstmt.close();
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
先建立了到数据库的连接,然后准备了一个带占位符的SQL语句,设置了占位符的值,执行了SQL语句,遍历了结果集,最后关闭了结果集、预编译的语句和连接。
JDBC
JDBC概述
JDBC是Java环境中访问数据库的一组API,它由一些Java语言编写的类和接口组成,能方便地向任何关系型数据库发送SQL命令。
JDBC为数据库应用开发人员开发人员提供了一种标准的应用程序设计接口,使开发人员可以用纯Java语言编写完整的数据库应用程序。
操作不同的数据库仅仅是连接方式上的差异而已。
使用JDBC的应用程序一旦与数据库建立连接,就可以使用JDBC提供的API操作数据库。
JDBC数据库进行交互
在JDBC中,要与数据库进行交互,首先要加载并注册相应的数据库驱动。这是通过Driver
接口完成的。Driver
接口定义了一些方法,如connect()
,用于建立与数据库的连接。具体步骤如下:
-
加载驱动:使用
Class.forName()
方法加载数据库驱动。这个方法会查找并初始化指定类,如果该类实现了Driver
接口,那么就会调用其register()
方法,将其实例注册到DriverManager
中。javatry { Class.forName("com.mysql.jdbc.Driver"); } catch (ClassNotFoundException e) { e.printStackTrace(); }
-
创建连接:使用
DriverManager.getConnection()
方法创建与数据库的连接。javatry { Connection con = DriverManager.getConnection(url, user, password); } catch (SQLException e) { e.printStackTrace(); }
其中,
url
是数据库的URL,user
和password
是数据库的用户名和密码。
完整代码
java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class JDBCExample {
public static void main(String[] args) {
try {
// 加载并注册驱动
Class.forName("com.mysql.jdbc.Driver");
// 创建与数据库的连接
Connection con = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/mydatabase",
"username",
"password"
);
// 使用连接...
con.close(); // 关闭连接
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
}
}
大多数JDBC驱动程序都把初始化工作放在
Driver
接口实现类的静态初始化模块中,因此在使用驱动程序前一般要用Class.forName()
显式加载驱动程序。但有些驱动程序(如Oracle的JDBC驱动)则不需要这样做,可以直接使用DriverManager.getConnection()
方法。
DriverManager类
DriverManager
类是JDBC的核心类之一,主要负责管理数据库连接。它的主要功能包括:
- 注册驱动:当一个
Driver
接口的实现类被加载时,DriverManager
会自动调用其register()
方法,将其实例注册到DriverManager
中。 - 建立连接:
DriverManager
维护着一个已注册的Driver
列表,当需要建立数据库连接时,它会遍历这个列表,找到合适的Driver
来建立连接。
getConnection()
方法是DriverManager
的主要方法,用于获取数据库连接。它的常用重载版本有三个:
getConnection(String url)
:返回与给定URL的数据库的连接。
getConnection(String url, String user, String password)
:返回与给定URL的数据库的连接,并使用给定的用户名和密码。
getConnection(String url, Properties info)
:返回与给定URL的数据库的连接,并使用给定的属性。
使用getConnection()
方法获取与MySQL数据库的连接
java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class JDBCExample {
public static void main(String[] args) {
try {
// 加载并注册驱动
Class.forName("com.mysql.jdbc.Driver");
// 创建与数据库的连接
Connection con = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/mydatabase",
"username",
"password"
);
// 使用连接...
con.close(); // 关闭连接
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
}
}
getConnection()
方法的第一个参数是数据库的URL,第二个和第三个参数分别是数据库的用户名和密码。
创建Statement对象
发送SQL语句主要是通过Statement
对象完成的。Statement
对象由Connection
对象创建,代表一个数据库会话。Statement
对象主要用于执行静态SQL语句,并能返回执行结果。
Statement
接口中有许多方法,常用的有:
executeQuery(String sql)
:执行SQL查询语句,并返回一个ResultSet
对象。
executeUpdate(String sql)
:执行SQL更新语句(插入、删除、修改),并返回受影响的行数。
close()
:关闭Statement
对象。
例如,以下代码创建了一个Statement
对象,并执行了一个查询语句
java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class JDBCExample {
public static void main(String[] args) {
try {
// 加载并注册驱动
Class.forName("com.mysql.jdbc.Driver");
// 创建与数据库的连接
Connection con = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/mydatabase",
"username",
"password"
);
// 创建Statement对象
Statement stmt = con.createStatement();
// 执行查询语句
ResultSet rs = stmt.executeQuery("SELECT * FROM mytable");
// 处理结果集...
rs.close();
stmt.close();
con.close();
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
}
}
在这个例子中,
createStatement()
方法创建了一个Statement
对象,executeQuery()
方法执行了一个查询语句,并返回了一个ResultSet
对象,表示查询的结果。
Statement接口常用方法
Statement是Java数据库连接(JDBC)API中的一个接口,用于执行SQL语句,并返回结果。
-
ResultSet executeQuery(String sql) throws SQLException: 这个方法用于执行查询语句(SELECT),并将结果放在ResultSet对象中。ResultSet是一个游标模型,可以逐行访问结果集。
javaString sql = "SELECT * FROM employees"; Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(sql); while (rs.next()) { System.out.println(rs.getString("name") + ", " + rs.getInt("age")); }
-
int executeUpdate(String sql) throws SQLException: 这个方法用于执行修改或插入语句(INSERT, UPDATE, DELETE),并返回受影响的记录数。
javaString sql = "DELETE FROM employees WHERE age > 60"; int rowsAffected = stmt.executeUpdate(sql); System.out.println(rowsAffected + " records deleted");
-
boolean execute(String sql) throws SQLException: 这个方法用于执行任何类型的SQL语句,包括查询和更新。如果执行成功,返回true;否则返回false。
javaString sql = "CREATE TABLE employees (id INT PRIMARY KEY, name VARCHAR(50), age INT)"; boolean success = stmt.execute(sql); if (success) { System.out.println("Table created successfully"); } else { System.out.println("Failed to create table"); }
以上所有操作都需要一个有效的Connection对象conn。在实际应用中,你需要先通过DriverManager.getConnection()方法获取到数据库连接。
ResultSet接口
ResultSet是Java数据库连接(JDBC)API中的一个接口,表示从数据库查询得到的结果集。当执行了一个查询语句后,Statement或PreparedStatement对象会返回一个ResultSet对象,该对象包含了满足查询条件的所有行。
-
ResultSet类定义了访问执行Statement产生的结果集的方法:
-
next(): 移动光标到下一行。
-
getXXX(int columnIndex): 根据列号获取值。
-
getXXX(String columnName): 根据列名获取值
java// 假设我们有一个名为employees的表,包含id, name, age三列 String sql = "SELECT * FROM employees"; Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(sql); // 遍历结果集 while (rs.next()) { // 获取第1列(记住,列是从1开始计数的) int id = rs.getInt(1); // 或者,你可以使用列名来获取值 String name = rs.getString("name"); int age = rs.getInt("age"); System.out.println(id + ": " + name + ", " + age); } // 注意:一定要关闭资源 rs.close(); stmt.close();
-
-
结果集一般是一个表,其中有查询返回的列标题及相应的值:
-
列标题可以通过getMetaData()方法获得,它返回一个ResultSetMetaData对象,其中包含了列的数量和名称。
javaResultSetMetaData meta = rs.getMetaData(); for (int i = 1; i <= meta.getColumnCount(); i++) { System.out.print(meta.getColumnName(i) + "\t"); } System.out.println(); while (rs.next()) { for (int i = 1; i <= meta.getColumnCount(); i++) { System.out.print(rs.getObject(i) + "\t"); } System.out.println(); }
-
查询操作
与数据库建立连接后,就可以使用JDBC提供的API和数据库交互信息,比如查询、修改和更新数据库中的表等。
java
import java.sql.*;
public class JdbcExample {
public static void main(String[] args) {
try {
// 加载驱动
Class.forName("com.mysql.jdbc.Driver");
// 创建数据库连接
Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/mydatabase",
"username",
"password"
);
// 创建Statement对象
Statement stmt = conn.createStatement();
// 执行查询语句
ResultSet rs = stmt.executeQuery("SELECT * FROM employees");
// 处理查询结果
while (rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
int age = rs.getInt("age");
System.out.println(id + ": " + name + ", " + age);
}
// 关闭资源
rs.close();
stmt.close();
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
首先加载MySQL的JDBC驱动,然后创建一个数据库连接。接着,我们创建一个Statement对象,并使用它来执行一个查询语句。最后,我们遍历查询结果,并打印出每一行的数据。
JDBC与数据库表进行交互的主要方式是使用SQL语句,JDBC提供的API可以将标准的SQL语句发送给数据库,实现与数据库的交互
java
try {
PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM employees WHERE name = ?");
pstmt.setString(1, "John Doe"); // 设置第一个参数为"John Doe"
ResultSet rs = pstmt.executeQuery();
while (rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
int age = rs.getInt("age");
System.out.println(id + ": " + name + ", " + age);
}
rs.close();
pstmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
使用PreparedStatement来执行一个带有参数的查询语句。我们首先准备一个SQL语句,然后设置参数,最后执行查询。这样做的好处是可以避免SQL注入攻击,因为所有的输入都会被正确地转义。
处理查询结果的方法
ResultSet对象一次只能看到一个数据行,使用next()方法使光标走到下一行,获得一行数据后,只要将位置索引或字段名传递给getXxx方法就可获得字段值。
java
ResultSet rs = stmt.executeQuery("SELECT * FROM employees");
while (rs.next()) { // 每次调用next()方法,光标就会移动到下一行
int id = rs.getInt(1); // 使用位置索引来获取第一列的值
String name = rs.getString("name"); // 使用字段名来获取第二列的值
System.out.println(id + ": " + name);
}
首先执行一个查询语句,然后遍历结果集。每次调用next()方法,光标就会移动到下一行。我们使用位置索引(从1开始)或字段名来获取每行的值。
无论字段是何种属性,都可以使用getString(col)方法返回字段值的字符串表示
java
ResultSet rs = stmt.executeQuery("SELECT * FROM employees");
while (rs.next()) {
String idStr = rs.getString(1); // 将整型字段转换为字符串
String name = rs.getString("name"); // 直接获取字符串字段
System.out.println(idStr + ": " + name);
}
使用getString()方法来获取每个字段的字符串表示。即使字段是整型或其他类型,这个方法也能正常工作。
当使用getXxx方法查看一行记录时,不可以颠倒字段的顺序。
java
ResultSet rs = stmt.executeQuery("SELECT * FROM employees");
while (rs.next()) {
String name = rs.getString("name"); // 先获取name字段
int id = rs.getInt(1); // 再获取id字段
System.out.println(id + ": " + name);
}
在这个例子中,我们不能先获取id字段,然后再获取name字段,因为一旦调用了getString()方法,光标就已经移动到了下一行。所以,我们必须按照正确的顺序来获取字段。
模糊查询
条件查询:通过在SQL语句中WHERE子句中指定查询条件来实现条件查询
java
String sql = "SELECT * FROM employees WHERE salary > 5000"; // 查询工资大于5000的员工
ResultSet rs = stmt.executeQuery(sql);
while (rs.next()) {
int id = rs.getInt(1);
String name = rs.getString("name");
double salary = rs.getDouble("salary");
System.out.println(id + ": " + name + ", " + salary);
}
使用WHERE子句来限制查询结果。只有满足条件的行才会出现在结果集中。
模糊查询:通过用SQL语句操作符LIKE进行模式匹配,使用%代替0个或多个字符,用下划线_代替一个字符,来实现模糊查询。
java
String sql = "SELECT * FROM employees WHERE name LIKE '张%'; // 查询名字以'张'开头的员工
ResultSet rs = stmt.executeQuery(sql);
while (rs.next()) {
int id = rs.getInt(1);
String name = rs.getString("name");
System.out.println(id + ": " + name);
}
使用LIKE操作符来进行模糊查询。%代表任意数量的字符,所以我们找到了所有名字以'张'开头的员工。
java
String sql = "SELECT * FROM employees WHERE name LIKE '_明%'; // 查询名字第二个字是'明'的员工
ResultSet rs = stmt.executeQuery(sql);
while (rs.next()) {
int id = rs.getInt(1);
String name = rs.getString("name");
System.out.println(id + ": " + name);
}
使用_来代替一个字符,所以我们找到了所有名字第二个字是'明'的员工。
更新、添加与删除操作
通过Statement对象调用executeUpdate(SQL语句)执行更新、添加和删除记录的SQL语句来实现更新、添加和删除等操作。
java
// 更新一条记录
String sql = "UPDATE employees SET salary = 8000 WHERE name='张三'";
int rowsAffected = stmt.executeUpdate(sql); // 返回受影响的行数
// 添加一条记录
sql = "INSERT INTO employees(name, salary) VALUES('李四', 7000)";
rowsAffected = stmt.executeUpdate(sql);
// 删除一条记录
sql = "DELETE FROM employees WHERE name='王五'";
rowsAffected = stmt.executeUpdate(sql);
使用Statement对象的executeUpdate()方法来执行更新、添加和删除操作。这个方法会返回受影响的行数,如果成功则返回1,否则返回0
更新、添加和删除记录的SQL语法:
- UPDATE <表名> SET <字段名>=新值 WHERE <条件子句>
- INSERT INTO 表(字段列表) VALUES (对应的具体记录)
- DELETE FROM <表名> WHERE <条件子句>
java
UPDATE employees SET salary = 8000 WHERE name='张三';
INSERT INTO employees(name, salary) VALUES('李四', 7000);
DELETE FROM employees WHERE name='王五';
这些是基本的SQL语句,用于更新、添加和删除记录。注意,添加记录时,如果没有指定字段列表,则需要提供完整的记录,包括所有字段的值。
事务
事务由一组SQL语句组成,所谓"事务处理"是指:应用程序保证事务中的SQL语句要么全部都执行,要么一个都不执行。
java
Connection con = ...; // 假设已经获得了数据库连接
con.setAutoCommit(false); // 关闭自动提交
try {
con.createStatement().executeUpdate("UPDATE employees SET salary = 8000 WHERE name='张三'");
con.createStatement().executeUpdate("INSERT INTO employees(name, salary) VALUES('李四', 7000)");
con.commit(); // 提交事务
} catch (SQLException e) {
con.rollback(); // 回滚事务
}
关闭了自动提交,然后执行了一组SQL语句。如果其中任何一个语句失败,那么整个事务都会回滚,即恢复到事务开始之前的状态。如果所有语句都成功,我们就提交事务,让它们生效。
事务是保证数据库中数据完整性与一致性的机制。JDBC事务处理步骤如下:
-
setAutoCommit(boolean autoCommit)方法:为了能进行事务处理,必须关闭连接对象 con 的默认设置。
-
commit()方法:连接对象 con 调用commit()方法就是让事务中的SQL语句全部生效。
-
rollback()方法:连接对象 con 调用rollback()方法,撤销引起数据发生变化的SQL语句操作,将数据库中的数据恢复到commit()方法执行之前的状态。
javacon.setAutoCommit(false); // 关闭自动提交 try { con.createStatement().executeUpdate("UPDATE employees SET salary = 8000 WHERE name='张三'"); con.createStatement().executeUpdate("INSERT INTO employees(name, salary) VALUES('李四', 7000)"); con.commit(); // 提交事务 } catch (SQLException e) { con.rollback(); // 回滚事务 }