Copyright © 2020 @Linyer. All Rights Reserved.
目录
初识JSP
JSP 输出 和 注释
- Java代码写在
<% %>
里面,称为Java小脚本。 - 设置 JSP 页面属性:
<%@ page %>
- 在页面输出一句话:
<% out.print("<h1>Java Web 笔记!</h1>"); %>
- 变量输出:
<% String head = "Java Web 笔记!" %>
<h1><%=head%></h1>
- HTML 注释:
<!-- html注释 -->
- JSP 注释:
<%-- out.print("<h1>Java Web 笔记!</h1>"); --%>
- JSP 中脚本注释:
<%
//单行注释!
/*多行注释!*/
%>
JSP 中使用变量
<%@ page import="java.text.SimpleDateFormat" %>
<%
String title = "Java Web 笔记!";
//当前系统时间
Date date = new Date();
//格式化输出时间
SimpleDateFormat fromater = new SimpleDateFormat("yyyy-MM-dd");
String time = fromater.format(date);
%>
<h1><%=title %></h1>
<h1>当前时间为:<%=time %></h1>
JSP 声明全局变量 和 方法
- 全局变量每刷新一次都加1
<h1><%=title %></h1>
当前时间为:<%=time %>
<%
//局部变量
int i = 2;
%>
<h1><%=i++ %></h1>
<%!
//全局变量
int j = 6;
%>
<h1><%=j++ %></h1>
- 方法要写全局形式
<%!
public int add() {
return 8+8;
}
%>
<%=add() %>
Web 程序的错误
- 404 错误:找不到访问的页面或资源。
- 500 错误:JSP页面语法有误。
JSP 实现 数据传递 和 保存
获取请求中的数据
- 发送请求页面 Regist.jsp
- 外面包上
<form></form>
发送数据
- 外面包上
<h1 align="center"><font color="green">用户注册</font></h1>
<form name="registerFrm" id="registFrm" action="doRegist.jsp" method="get">
<table class="tb" border="0" cellspacing="5" cellpadding="0" align="center">
<tr>
<td class="text_tabledatil2">用户名</td>
<td><input type="text" name="username"/></td>
</tr>
<tr>
<td class="text_tabledatil2">密码</td>
<td><input type="password" name="password"/></td>
</tr>
<tr>
<td class="text_tabledatil2">确认密码</td>
<td><input type="password" name="con_password"/></td>
</tr>
<tr>
<td class="text_tabledatil2">e-mail</td>
<td><input type="text" name="email"/></td>
</tr>
<tr>
<td class="text_tabledatil2">爱好</td>
<td>
<input type="checkbox" name="hobby" value="游泳"/>游泳<br/>
<input type="checkbox" name="hobby" value="阅读"/>阅读<br/>
<input type="checkbox" name="hobby" value="爬山"/>爬山<br/>
<input type="checkbox" name="hobby" value="旅游"/>旅游<br/>
</td>
</tr>
<tr>
<td style="text-align:center" colspan="2">
<button type="submit" class="page-btn" name="save">注册</button>
</td>
</tr>
</table>
</form>
- 方法:
public String getParameter(String name)
- 接受请求页面 doRegist.jsp
<%
String userName = request.getParameter("username");
String pwd = request.getParameter("password");
String email = request.getParameter("email");
String[] hobby = request.getParameterValues("hobby");
%>
用户名:<%=userName %><br/>
密码 :<%=pwd %><br/>
email:<%=email %><br/>
用户爱好:
<%
if(hobby!=null&&hobby.length!=0){
for(String hobbys:hobby){
out.print(hobbys+" ");
}
}else{
out.print("没选爱好!");
}
%>
转发 与 重定向
- request 的作用域:一次请求
- 保存属性方法:
public void setAttribute(String name,Object o)
要对 name 做非空判断!
- 获取属性方法:
public Object getAttribute(String name)
- 转发:使用 RequestDispatcher 对象中的 forward() 方法
request.getRequestDispatcher("url").forward(request,response)
- 重定向:将用户请求重新定位到一个新的 URL
request.sendRedirect("url")
- 转发与重定向的区别
比较项目 | 转发 | 重定向 |
---|---|---|
URL 变化 | 否 | 是 |
重新发出请求 | 不会 | 会 |
是否携带请求 | 是 | 否 |
目标 URL 要求 | 仅本 Web 应用 | 任意 URL |
- doRegist.jsp
if(!pwd.equals(c_pwd)) {
request.setAttribute("mess","两次输入密码不一致,注册失败!");
request.getRequestDispatcher("Regist.jsp").forward(request, response);
}else{
String info = "sucess";
response.sendRedirect("index.jsp?info="+info);
}
- Regist.jsp
Object omess = request.getAttribute("mess");
if(omess!=null){
out.print(omess.toString());
}
- index.jsp
if(request.getParameter("info").equals("sucess")){
out.print("<h1 align=\"center\"><font color=\"green\">恭喜你!注册成功!</font></h1>");
}
JSP 内置对象
- JSP 已经准备好的,可以直接使用的对象
对象 | 语句 |
---|---|
请求对象 | request |
输出对象 | out |
响应对象 | response |
应用程序对象 | application |
会话对象 | session |
页面上下文对象 | pageContext |
页面对象 | page |
配置对象 | config |
异常对象 | exception |
session 的使用
- session 作用域:一次会话
- 会话:一个会话就是浏览器和服务器之间的一次通话
- 会话 可以在多次请求中保存和使用数据
- 修改 doRegister.jsp 中 else 后面为:
else{
session.setAttribute("userName",userName);
response.sendRedirect("index.jsp");
}
会话的 清除 和 过期
程序主动清除 session 数据
- 设置会话失效:
session.invalidate();
- 移除会话的一个属性:
public void remoteAttribute(String name);
- 用法:
session.remoteAttribute("userNmae");
服务器主动清除长时间没有再次发出请求的 session
- 设置会话过期时间:
- 方法一:
public void setMaxInactiveInterval(int interval);
设置最大活动时间,单位为 秒!
- 方法二:
<session-config>
<session-timeout>30</session-timeout>
</session-config>
- 单位为 分钟
- 在
web.xml
中设置!
- Loginout.jsp
//用户注销会话
session.invalidate();
response.sendRedirect("index.jsp");
cookie 的使用
- cookie:跟踪用户的整个会话
- 在客户端记录信息,给客户端发送一个通行证,每个客户一个。
- 本质是一小段文本信息。
- 以文件的形式保存数据。
- 添加数据
public void addCookie(Cookie cookie);
- 获取数据
public Cookie[] getCookie();
- 设置路径
cookie.setPath("/");
- 整个工程无论什么路径都能访问 cookie
- session 基于 cookie
utf-8
中文 编码 解码
userName = URLEncoder.encode(userName,"utf-8");
userName = URLDecoder.decode(userName,"utf-8");
- doRegist.jsp
Cookie cookie = new Cookie("userName",userName);
cookie.setMaxAge(60*60);
response.addCookie(cookie);
application 的使用
- application 作用域:整个应用程序
- 统计页面访问次数
//统计该页面的访问次数
Object count = application.getAttribute("count");
if(count==null){
//第一次访问
application.setAttribute("count", 1);
}else{
//非第一次访问
Integer i = (Integer)count;
application.setAttribute("count", i+1);
}
Integer icount = (Integer)application.getAttribute("count");
out.print("访问次数为:"+icount);
三个对象对比
- request、session、application
- 相同点:都可以存储属性
- 不同点:
- request 中存储的数据仅在一个请求中可用
- session 中存储的数据在一个会话的有效期内可用
- application 中存储的数据在整个Web项目中可用
使用 JDBC 操作数据库
JDBC 使用步骤
- JDBC API
- 实现 Java 程序对各种数据库的访问
- 一组接口和类,位于 java.sql 与 javax.sql 包
- 面向接口编程
- JDBC 步骤固定!
- Class.forName(String) 加载驱动
- DriverManage 获取 Connection 连接
- 创建 Statemen 或 PrepardStatement 对象执行 SQL 语句
- 返回 ResultSet 查询结果
- 释放资源(先用先关,后用后关)
使用 JDBC 实现查询
- 查询新闻 id、标题
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import com.mysql.jdbc.Connection;
import com.mysql.jdbc.Statement;
public class NewsDao {
//查询新闻id、标题
public static void main(String[] args) {
//写在外面,可以在finally里关
Connection connection = null;
Statement stmt = null;
ResultSet rs = null;
try {
//Class.forName(String) 加载驱动
Class.forName("com.mysql.jdbc.Driver");
//DriverManage 获取 Connection 连接
String url = "jdbc:mysql://localhost:9988/kgcnews?characterEncoding=utf8&useSSL=true";
connection = (Connection) DriverManager.getConnection(url,"root","123456");
// SQL 语句
String sql = "select id,title from news_detail;";
//创建 Statemen对象执行 SQL 语句
stmt = (Statement)connection.createStatement();
//返回 ResultSet 查询结果
rs = stmt.executeQuery(sql);
while (rs.next()) {
int id = rs.getInt("id");
String title = rs.getString("title");
System.out.println(id+"\t"+title);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
//释放资源
try {
rs.close();
stmt.close();
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
使用 PrepardStatement
- Statement 与 PreparedStatement 区别
- PreparedStatement 接口继承 Statement
Statement st = connection.createStatement();
PrepareStatement pstm = connection.prepareStatement(sql);
-
SQL语句使用 “?” 作为数据占位符
使用 setXxx() 方法设置数据 - PreparedStatement——预编译
- 效率、性能、开销
- 安全性(PreparedStatement可以防止一定的SQL注入)
- 代码可读性
- 查询特定标题的新闻信息
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import com.mysql.jdbc.Connection;
import com.mysql.jdbc.PreparedStatement;
public class NewsDao {
//查询特定标题的新闻信息
public void getNewsByTitle(String title){
//写在外面,可以在finally里关
Connection connection = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
//Class.forName(String) 加载驱动
Class.forName("com.mysql.jdbc.Driver");
//DriverManage 获取 Connection 连接
String url = "jdbc:mysql://localhost:9988/kgcnews?characterEncoding=utf8&useSSL=true";
connection = (Connection) DriverManager.getConnection(url,"root","123456");
// SQL 语句,?叫占位符
String sql = "select * from news_detail where title=?";
//创建PreparedStatement对象执行 SQL 语句
pstmt = (PreparedStatement) connection.prepareStatement(sql);
//在sql语句的第一个问号的位置填充title
pstmt.setString(1, title);
//返回 ResultSet 查询结果,不用再传sql了
rs = pstmt.executeQuery();
while (rs.next()) {
int id = rs.getInt("id");
String newsTitle = rs.getString("title");
System.out.println(id+"\t"+newsTitle);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
//释放资源
try {
rs.close();
pstmt.close();
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
NewsDao dao = new NewsDao();
dao.getNewsByTitle("Java Web开课啦");
}
}
实现数据 增删改 操作
- 示例:
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Date;
import com.mysql.jdbc.Connection;
import com.mysql.jdbc.PreparedStatement;
//用 JDBC 实现新闻数据 增 删 改 操作
public class NewsDao{
Connection connection = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
//获取数据库连接
public void getConnection(){
try {
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:9988/kgcnews?characterEncoding=utf8&useSSL=true";
connection = (Connection) DriverManager.getConnection(url,"root","123456");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}catch (SQLException e) {
e.printStackTrace();
}
}
//关闭数据库连接
public void closeConection(){
try {
pstmt.close();
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
//增加新闻信息
public void addNews(int id,int categoryid,String title,String summary,String content,String author,Date createdate){
try {
this.getConnection();
String sql = "INSERT INTO news_detail(id,categoryid,title,summary,content,author,createdate) VALUES(?,?,?,?,?,?,?)";
pstmt = (PreparedStatement) connection.prepareStatement(sql);
pstmt.setInt(1,id);
pstmt.setInt(2,categoryid);
pstmt.setString(3,title);
pstmt.setString(4,summary);
pstmt.setString(5,content);
pstmt.setString(6,author);
pstmt.setTimestamp(7, new Timestamp(createdate.getTime()));
int i = pstmt.executeUpdate();
if(i > 0){
System.out.println("插入新闻成功!");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
this.closeConection();
}
}
//删除特定新闻标题
public void deleteNews(int id){
try {
this.getConnection();
String sql = "DELETE FROM news_detail WHERE id=?";
pstmt = (PreparedStatement) connection.prepareStatement(sql);
pstmt.setInt(1, id);
int i = pstmt.executeUpdate();
if(i > 0){
System.out.println("删除新闻成功!");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
this.closeConection();
}
}
//修改特定新闻标题的方法
public void updateNews(int id,String title){
try {
this.getConnection();
String sql = "UPDATE news_detail SET title=? WHERE id=?";
pstmt = (PreparedStatement) connection.prepareStatement(sql);
pstmt.setString(1, title);
pstmt.setInt(2, id);
int i = pstmt.executeUpdate();
if(i > 0){
System.out.println("修改新闻成功!");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
this.closeConection();
}
}
//查询全部新闻信息
public void getNewsList(String title){
try {
this.getConnection();
String sql = "select id,categoryid,title,summary,content,author,createdate from news_detail";
pstmt = (PreparedStatement) connection.prepareStatement(sql);
rs = pstmt.executeQuery();
while (rs.next()) {
int id = rs.getInt("id");
int categoryid = rs.getInt("categoryid");
String newsTitle = rs.getString("title");
String summary = rs.getString("summary");
String content = rs.getString("content");
String author = rs.getString("author");
Timestamp createdate = rs.getTimestamp("createdate");
System.out.println(id+"\t"+categoryid+"\t"+newsTitle+"\t"+summary+"\t"+content+"\t"+author+"\t"+createdate);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
this.closeConection();
}
}
//查询特定标题的新闻信息
public void getNewsByTitle(String title){
try {
this.getConnection();
String sql = "select * from news_detail where title=?";
//创建PreparedStatement对象执行 SQL 语句
pstmt = (PreparedStatement) connection.prepareStatement(sql);
//在sql语句的第一个问号的位置填充title
pstmt.setString(1, title);
//返回 ResultSet 查询结果,不用再传sql了
rs = pstmt.executeQuery();
while (rs.next()) {
int id = rs.getInt("id");
String newsTitle = rs.getString("title");
System.out.println(id+"\t"+newsTitle);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
//释放资源
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
this.closeConection();
}
}
public static void main(String[] args) {
NewsDao dao = new NewsDao();
// dao.addNews(3, 1, "test", "test", "test", "Linyer", new Date());
// dao.deleteNews(3);
// dao.updateNews(3, "newTitle");
// dao.getNewsList("Java Web开课啦");
dao.getNewsByTitle("Java Web开课啦");
}
}
DAO 模式 及 单例模式
DAO 组件的优化思路
- 操作:
- 提取数据库公共操作(获取数据库连接、释放资源、增删改、查)形成一个数据库操作的基类。
- 将 NewsDao 提取一个稳定的新闻操作的接口,新闻操作写在接口的实现类中。
- 作用:
- 将相似功能的代码抽取封装成方法,减少代码冗余。
- 因为不同的数据库会有不同的实现,对数据库的操作一般抽取成接口,在以后的开发中可以降低耦合。
DAO 操作的基类
- BaseDao.java 示例(放在dao包中):
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import com.mysql.jdbc.Connection;
import com.mysql.jdbc.PreparedStatement;
//数据库操作的基类
public class BaseDao {
Connection connection = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
//获得数据库资源
public boolean getConnection(){
try {
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:9988/kgcnews?characterEncoding=utf8&useSSL=true";
connection = (Connection) DriverManager.getConnection(url,"root","123456");
} catch (ClassNotFoundException e) {
e.printStackTrace();
return false;
}catch (SQLException e) {
e.printStackTrace();
return false;
}
return true;
}
//增删改
public int executeUpdate(String sql,Object[] params) {
int updateRows = 0;
if(this.getConnection()){
try {
pstmt = (PreparedStatement) connection.prepareStatement(sql);
//填充占位符
for(int i=0;i<params.length;i++){
pstmt.setObject(i+1, params[i]);
}
updateRows = pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
return updateRows;
}
//查询
public ResultSet executeSQL(String sql,Object[] params) {
if(this.getConnection()){
try {
pstmt = (PreparedStatement) connection.prepareStatement(sql);
//填充占位符
for(int i=0;i<params.length;i++){
pstmt.setObject(i+1, params[i]);
}
rs = pstmt.executeQuery();
} catch (SQLException e) {
e.printStackTrace();
}
}
return rs;
}
//释放资源
public boolean closeResource(){
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
return false;
}
}
if(pstmt!=null){
try {
pstmt.close();
} catch (SQLException e) {
e.printStackTrace();
return false;
}
}
if(connection!=null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
return false;
}
}
return true;
}
}
面向接口的 DAO 设计
- NewsDao.java 示例(放在dao包中):
import java.util.Date;
public interface NewsDao{
//增加新闻信息
public void addNews(int id,int categoryid,String title,String summary,String content,String author,Date createdate);
//删除特定新闻标题
public void deleteNews(int id);
//修改特定新闻标题的方法
public void updateNews(int id,String title);
//查询全部新闻信息
public void getNewsList(String title);
//查询特定标题的新闻信息
public void getNewsByTitle(String title);
}
- NewsDaoImpl.java 示例(放在dao.impl包中):
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Date
//用 JDBC 实现新闻数据 增 删 改 操作
public class NewsDaoImpl extends BaseDao implements NewsDao{
//增加新闻信息
public void addNews(int id,int categoryid,String title,String summary,String content,String author,Date createdate){
try {
String sql = "INSERT INTO news_detail(id,categoryid,title,summary,content,author,createdate) VALUES(?,?,?,?,?,?,?)";
Object params[] = {id,categoryid,title,summary,content,author,createdate};
int i = this.executeUpdate(sql,params);
if(i > 0){
System.out.println("插入新闻成功!");
}
} finally {
this.closeResource();
}
}
//删除特定新闻标题
public void deleteNews(int id){
try {
String sql = "DELETE FROM news_detail WHERE id=?";
Object params[] = {id};
int i = this.executeUpdate(sql,params);
if(i > 0){
System.out.println("删除新闻成功!");
}
} finally {
this.closeResource();
}
}
//修改特定新闻标题的方法
public void updateNews(int id,String title){
try {
String sql = "UPDATE news_detail SET title=? WHERE id=?";
Object params[] = {id,title};
int i = this.executeUpdate(sql,params);
if(i > 0){
System.out.println("修改新闻成功!");
}
} finally {
this.closeResource();
}
}
//查询全部新闻信息
public void getNewsList(String title){
try {
String sql = "select id,categoryid,title,summary,content,author,createdate from news_detail";
Object params[] = {};
ResultSet rs = this.executeSQL(sql, params);
while (rs.next()) {
int id = rs.getInt("id");
int categoryid = rs.getInt("categoryid");
String newsTitle = rs.getString("title");
String summary = rs.getString("summary");
String content = rs.getString("content");
String author = rs.getString("author");
Timestamp createdate = rs.getTimestamp("createdate");
System.out.println(id+"\t"+categoryid+"\t"+newsTitle+"\t"+summary+"\t"+content+"\t"+author+"\t"+createdate);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
this.closeResource();
}
}
//查询特定标题的新闻信息
public void getNewsByTitle(String title){
try {
String sql = "select * from news_detail where title=?";
Object params[] = {0};
ResultSet rs = this.executeSQL(sql, params);
while (rs.next()) {
int id = rs.getInt("id");
String newsTitle = rs.getString("title");
System.out.println(id+"\t"+newsTitle);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
this.closeResource();
}
}
public static void main(String[] args) {
NewsDao dao = new NewsDaoImpl();
// dao.addNews(3, 1, "test", "test", "test", "Linyer", new Date());
// dao.deleteNews(3);
// dao.updateNews(3, "newTitle");
// dao.getNewsList("Java Web开课啦");
dao.getNewsByTitle("Java Web开课啦");
}
}
使用属性文件管理数据
- 据库发生改变时,需要重新修改代码,重新编译和部署。
- 将数据库信息写在配置文件中,让程序通过读取配置文件来获取这些信息。
- 以
.properties
为后缀的文件就是配置文件,存储的信息为键值对的形式。
- database.properties 示例(于 src 目录下):
jdbc.driver=com.mysql.jdbc.driver
jdbc.connection.url=jdbc:mysql://localhost:9988/kgcnews?characterEncoding=utf8&useSSL=true
jdbc.connection.username=root
jdbc.connection.password=123456
- ConfigManager.java 示例(于 util 包中):
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
//读取数据库属性文件,获取数据库连接信息
public class ConfigManager {
private Properties properties;
public ConfigManager(){
String configFile = "database.properties";
InputStream in = ConfigManager.class.getClassLoader().getResourceAsStream(configFile);
try {
properties.load(in);
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//根据属性文件中的键获得对应的值
public String getString(String key){
return properties.getProperty(key);
}
}
单例模式
- 如何让用户只创建一个 ConfigManager
- 把构造方法私有。
- 程序提供给别人一个对象。
- 分两种模式:懒汉模式 / 饿汉模式
- 区别在于 new 对象的时机不同。
- 懒汉模式
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
//读取数据库属性文件,获取数据库连接信息
public class ConfigManager {
private static ConfigManager configManager;
private Properties properties;
private ConfigManager(){
String configFile = "database.properties";
InputStream in = ConfigManager.class.getClassLoader().getResourceAsStream(configFile);
try {
properties.load(in);
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//提供给比儿一个唯一的ConfigManager对象
public static synchronized ConfigManager getInstance(){
if(configManager == null){
configManager = new ConfigManager();
}
return configManager;
}
//根据属性文件中的键获得对应的值
public String getString(String key){
return properties.getProperty(key);
}
}
- 饿汉模式
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
//读取数据库属性文件,获取数据库连接信息
public class ConfigManager {
private static ConfigManager configManager = new ConfigManager();
private Properties properties;
private ConfigManager(){
String configFile = "database.properties";
InputStream in = ConfigManager.class.getClassLoader().getResourceAsStream(configFile);
try {
properties.load(in);
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//提供给比儿一个唯一的ConfigManager对象
public static ConfigManager getInstance(){
return configManager;
}
//根据属性文件中的键获得对应的值
public String getString(String key){
return properties.getProperty(key);
}
}
数据源以及分层开发
理解和使用数据源
- 数据源
javax.sql.DataSource
接口负责建立与数据库的连接- 由 Tomecat 提供,将连接保存在连接池中
1、数据源用来连接数据库,获得连接(Connection)对象
2、连接池用来管理连接(Connection)对象
3、在程序中使用 JNDI 获取数据源
JNDI --> DataSource --> Connection --> 连接池
- 配置 tomcat 的 conf 中的 context.xml
<Resource name="jdbc/news"
auth="Container" type="javax.sql.DataSource" maxActive="100"
maxIdle="30" maxWait="10000" username="root" password="123456"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://127.0.0.1:9988/kgcnews?characterEncoding=utf8&useSSL=true"/>
- 获取数据库连接
//获取数据库连接
public boolean getConnection2(){
try {
//初始化上下文
Context cxt = new InitialContext();
//获取与逻辑名称相关联的数据源对象
DataSource ds = (DataSource) cxt.lookup("java:comp/env/jdbc/news");
//通过数据源获取数据库连接
connection = (Connection) ds.getConnection();
} catch (NamingException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
使用 JavaBean 封装数据
- JavaBean
- 就是一个 Java 类
- 封装业务逻辑
- 封装数据
public void add(int id,int categoryld,String title,String summary,String content,Date createdate){
//方法体
}
public void add(新闻信息对象){
//方法体
}
如:
import java.sql.Date;
//新闻JavaBean,仅封装数据(属性、setter及getter)
public class News {
private int id;
private int categoryId;
private String title;
private String summary;
private String content;
private String picPath;
private String author;
private Date creatDate;
private Date modifyDate;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getCategoryId() {
return categoryId;
}
public void setCategoryId(int categoryId) {
this.categoryId = categoryId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getSummary() {
return summary;
}
public void setSummary(String summary) {
this.summary = summary;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getPicPath() {
return picPath;
}
public void setPicPath(String picPath) {
this.picPath = picPath;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public Date getCreatDate() {
return creatDate;
}
public void setCreatDate(Date creatDate) {
this.creatDate = creatDate;
}
public Date getModifyDate() {
return modifyDate;
}
public void setModifyDate(Date modifyDate) {
this.modifyDate = modifyDate;
}
}
分层开发思路
- dao 层 —> 数据访问接口层
- 主要负责和数据操作相关的事情接口:NewsDao
- 接口实现类:NewsDaoImpl
- Service 层 —> 业务逻辑层主要负责与业务逻辑相关操作,对dao层的封装和调用
- 接口:NewsService
- 接口实现类:NewsServiceImpl
JSP 标签
- JSP 动作标签
- 通过动作标签,程序员可以在 JSP 页面中把页面的显示功能部分封装起来,使整个页面更简洁和易于维护
useBean
<jsp:useBean>
- 装载一个将在 JSP 页面中使用的 JavaBean,发挥 Java 组件重用的优势
<jsp:useBean id="name" class="package.class" scope="scope">
注释
- id–>JavaBean的引用名
- class–>JavaBean的类
- scope–>JavaBean的范围
include
<jsp:include>
- 把指定文件插入正在生成的页面中
<jsp:include page="URL">
- include 指令
<%@include file="URL"%>
<%@include%>
与<jsp:include>
区别:<jsp:include>
为动态包含,将被包含页面的结果包含进来。
先处理,再包含<%@include%>
为静态包含,将被包含页面的内容包含进来。
先包含,再处理
页面跳转
<jsp:forward>
<jsp:forward page="URL">
Servlet 和 过滤器
Servlet 概述
- Servlet 做了什么?
- 本身不做任何业务处理
- 只是接收请求并决定调用哪个 JavaBean 去处理请求
- 确定用哪个页面来显示处理返回的数据
- Servlet 是什么?
- Server+Applet,是一种服务器端的 Java 应用程序
- 只有当一个服务器端的程序使用了 Servlet API 的时候,这个服务端的程序才能称之为 Servlet
- Servlet 特点
- Java 程序
- 运行在服务器端
- 需要 web 容器的支持
- 使用了 ServletAPI
- 本身不做任何业务处理,中间调度作用
Servlet API
javax.servlet.Servlet
接口- 所有Java Servlet的基础接口类,规定了必须由Servlet具体类实现的方法集
javax.servlet.GenericServlet
类- 是 Servlet 的通用版本,是一种与协议无关的 Servlet
javax.servlet.http.HttpServlet
类 (使用较多)- 在 GenericServlet 基础上扩展的基于 Http 协议的 Servlet
- init()
- service()
- doGet()
- doPost()
- destroy()
Servlet 中主要方法
- init(): servlet 的初始化方法,仅仅会执行一次
- service(): 处理请求和生成响应
- destroy(): 在服务器停止并且程序中的Servlet对象不再使用的时候调用,只执行一次
- ServletRequest
- 封装客户的请求信息
- 作用相当于 JSP 内置对象 request
- ServletResponse
- 创建响应信息,将处理结果返回给客户端
- 作用相当于SP内置对象response
- ServletConfig: 包含了servlet的初始化参数信息
Servlet 生命周期
- Servlet 生命周期各个阶段
- 加载和实例化
- 初始化
- 处理请求
- 销毁
生命周期 | 谁来做 | 何时做 |
---|---|---|
实例化 | Servlet 容器 | 当 servlet 容器启动或者容器检测到客户端请求时 |
初始化 | servlet 容器 | 实例化后,容器调用 Servlet 的 init() |
初始化 | 对象处理请求 | Servlet 容器得到客户端请求并做出处理时 |
销毁 | servlet 容器 | 当程序中的 Servlet 对象不再使用的时候,或者 Web 服务器停止运行的时候 |
跳转路径问题
-
HttpServletResponse 重定向 response.sendRedirect()
http://localhost:8080/news/servlet/AddServlet
- (1)相对路径
response.sendRedirect("newsDetailList.jsp")
http://1ocalhost:8080/news/servlet/newsDetailList.jsp
- (2)绝对路径
response.sendRedirect("/news/jsp/admin/newsDetailList.jsp")
http://1ocalhost:8080/news/jsp/admin/newsDetailList.jsp
- (1)相对路径
-
HttpServletRequest 转发 request.getRequestDispatcher().forward()
http://1ocalhost:8080/news/servlet/AddServlet
- (1)相对路径
request.getRequestDispatcher("newsDetailList.jsp").forward()
http://localhost:8080/news/servlet/newsDetailList.jsp
- (2)绝对路径
response.sendRedirect("/news/jsp/admin/newsDetailList.jsp")
http://localhost:8080/news/jsp/admin/newsDetailList.jsp
- (1)相对路径
EL 与 JSTL
EL 表达式
EL 严格 区分 大小写,初学者严格按规范书写,有利于养成好的编码习惯
- EL 表达式(Expression Language)
- 语法
${EL表达式}
例如:${username}
- 语法
EL 操作符
- 操作符
.
(用的比较多)- 获取对象的属性,例如:
${news.title}
- 获取对象的属性,例如:
- 操作符
[]
- 获取对象的属性,例如:
${news["title"]}
- 获取集合中的对象,例如
newsList[0]
- 获取对象的属性,例如:
EL 功能
- 取得 JavaBean 对象的属性
${news.title}
- 取得 数组、List、Map 类型对象的元素
${ist[0]}
- 使用各类运算符对原始数据进行简单处理
${totalRecordCount/pageSizer}
- 屏蔽一些常见的异常
${username}
- 能实现简单的自动类型转换
${news}
相当于(News)request.getAttribute("news")
EL 访问作用域
request.setAttribute("news",news);
- 两种方式取数据:
- Java 小脚本:
request.getAttribute("news",news);
- 使用 EL 表达式:
${news}
或者${requestScope.news}
- Java 小脚本:
作用域 | Java代码取值 | EL取值 |
---|---|---|
请求作用域 | request.getAttribute("news"); | ${requestscope.news} |
会话作用域 | session.getAttribute("username"); | ${sessionscope.username} |
程序作用域 | application.getAttribute("count"); | ${applicationscope.count} |
页面作用域 | pageContext.getAttribute("userNum"); | ${pagescope.userNum} |
如果只有
${news}
会从作用域从小到大找
JSTL 表达式
- JSTL(JavaServerPages Standard Tag Library)
- JSP 标准标签库
- 实现 JSP 页面中的逻辑控制
- JSTL 使用步骤
- 下载 jstl.jar 和 standard.jar 包
http://archive.apache.org/dist/jakarta/taglibs/standard/binaries/
(点击以查看) - 将这两个包复制到
WEB-INF\lib
目录 - 在JSP页面中添加指令
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
- 下载 jstl.jar 和 standard.jar 包
JSTL 标签
标签库名称 | 资源标示符(uri) |
---|---|
*<c:out/> | 输出文本内容到out对象,常用于显示特殊字符,显示默认值 |
<c:set/> | 在作用域中设置变量或对象属性的值 |
<c.remove/> | 在作用域中移除变量的值 |
*<c:if/> | 实现条件判断结构 |
*<c:forEach/> | 实现循环结构 |
<c:url/> | 构造url地址 |
<c:param/> | 在url后附加参数 |
*<c:import/> | 在页面中嵌入另一个资源内容 |
*<fmt:formatDate/> | 格式化时间 |
<fmt:formatNumber/> | 格式化数字 |
带*
为常用加标签
分页查询
分页查询的后台实现
- 用 SQL 语句查询新闻总数
Select count(1)
From news_detail
- SQL语句限制显示的行数
分页查询新闻数据,每页显示两条数据,这里显示第 1 页:
SELECT id,title,author,createdate
WHERE 1=1
FROM news detail
LIMIT 0,2
分页查询新闻数据,每页显示两条数据,这里显示第 2 页:
SELECT id,title,author,createdate
WHERE 1=1
FROM news detail
LIMIT 2,2
分页查询新闻数据,每页显示两条数据,这里显示第 3 页:
SELECT id,title,author,createdate
WHERE 1=1
FROM news detail
LIMIT 4,2
- 公用的分页SQL:分页后每页显示几条新闻(页面容量):pageSize
从第几条数据开始显示(当前页码pageNo-1)*pageSize
SELECT id,title,author,createdate
WHERE 1=1
FROM news detail
LIMIT (pageNo-1)*pageSize,pageSize
圆角边框
border-radius: 20px 10px 50px 30px;