servlet
Jstl
Jstl标签库核心标签c标签
数据操作
保存数据
<%pageContext.setAttribute("name", "eric");%>
<c:set var="name" value="eric" scope="session"></c:set> 等价于上门
域对象获取数据
<%-- default: 当前value值为null时,使用默认值代替 escapeXml:是否转义 --%>
<c:out value="${name}" default="<h3>标题3</h3>" escapeXml="true"></c:out>
条件判断
<%--单条件 --%>
<c:if test="${10>4 }">
看不见远处的青山...
</c:if>
<%--多条件 --%>
<c:choose>
<c:when test="${empty loginInfo }">
请先登录或注册
</c:when>
<c:otherwise>
用户名:<c:out value="${loginInfo }"></c:out>,我的订单
</c:otherwise>
</c:choose>
循环语句 循环获取的是getXxx()的值
<%--循环
items: 需要遍历的集合
var: 每个对象的名称
begin:从哪个元素开始 0
end: 到哪个元素结束
step:每次递增多少
varStatus: 每个对象的状态(就是给前面加编号)
--%>
List循环
<c:forEach items="${list}" var="s" varStatus="vs">
序号: ${vs.count}, 姓名:${s.name },年龄:${s.age }<br/>
</c:forEach>
Map循环
<c:forEach items=”${map }” var “entry” >
${entry.value.name } ${entry.value.age }
</c:forEach>
按分隔符遍历
<%-- forTokens--%>
<c:set var="course" value="java-andorind-ios-php"></c:set>
<%-- delims: 分割符 --%>
<c:forTokens items="${course }" delims="-" var="c">
${c }<br/>
</c:forTokens>
url重定向
<a href="${pageContext.request.contextPath }/01.core.jsp">超链接</a><br/>
<a href="<c:url value='/01.core.jsp'/>">超链接</a><br/>
<%--重定向 --%>
<%--<c:redirect url="http://www.baidu.com"></c:redirect> --%>
Jsp动作标签
<jsp:forward/> 转发
<jsp:include/> 包含
<jsp:param/> 参数
静态包含: <%@inculude%>
1)被包含页面的变量可以在主页面上共享
2)被包含页面的名称不能使用变量
动态包含: <jsp:include/>
2)被包含页面的变量不能在主页面上共享
2)被包含页面的变量可以是变量
Javabean规范
1)必须有无参的构造方法
2)必须属性进行私有化
3)必须提供公开的getXxx和setXxx方法
注意:setXXX() getXXX() isXXX() (boolean类型的)
4)建议实现java.io.Serilizable接口
就是我们常用的实体对象
开发模式
Jsp +servlet+javabean =mvc模式
Mvc+三层架构(最终版)
包结构规范:
gz.itcast.empsys
|-entity 存放javabean类
|-dao 存放dao类
|-service 存放service类
|-web 存放servlet类
|-util 存放工具类
|-test 存放测试类
Jdbc
给sql输入值date时
需要转换
一
SimpleDateFormat sdf =new SimpleDateFormet(“yyyy-MM-dd’);
String curdate = Sdf.formet(new Date());
Stmt.setString(curdate);
二
setDate是sql语句需要传入sql的date时间,但是sql的date只能接受long类型,所以传入毫秒值。
Stmt.setDate(“位置”,new java.sql.Date(new java.util.Date().getTime()));
导入mysql数据库架包
|-Driver: 用于连接数据库
|-Connection: 用于创建不同类型的Statement
|-Statement: 用于执行静态sql语句
|-PreparedStatement: 用于执行预编译sql语句 (可以使用?代替参数)
|-ResultSet:用于封装数据库查询的结果
开发步骤:
1)注册驱动程序 (mysql)
2)获取连接
3)创建不同类型的Statement (Statement 或者 PreparedStatement)
4)发送sql语句
executeUpdate(sql) insert/update/delete/create/alter/drop
executeQuery(sql) *select/show
5) 得到结果,处理结果
int
ResultSet
next() 将光标移动到下一行
getXXX() 取出当前行的列值
6)关闭资源
Statement 接口 静态
//1)注册驱动程序 (注册驱动程序类)
Class.forName("com.mysql.jdbc.Driver");
//2)获取数据库连接
/**
* 参数一:url jdbc的连接字符串。
* web项目访问: http://localhost:8080/day16 url
* 协议 + 主机 + 端口 + web应用
* jdbc访问数据:jdbc:mysql://localhost:3306/day16
* 参数二:数据库用户名
* 参数三:数据库密码
*/
Connection conn =DriverManager.getConnection("jdbc:mysql://localhost:3306/day16", "root", "root");
//3)创建Statement对象
Statement stmt = conn.createStatement();
//4)发送sql语句
int count = stmt.executeUpdate("create table student(id int primary key,name varchar(20))");
//5)获取结果,处理结果
System.out.println("影响了"+count+"行");
//6)关闭连接
stmt.close();
conn.close();
PreparedStatement 接口 预编译
PreparedStatement stmt = conn.prepareStatement("insert into student(id,name) values(?,?)");
//预编译语句,使用?号,?号代表一个参数
//给参数赋值
stmt.setInt(1, 1);
stmt.setString(2, "狗娃");
//执行sql
stmt.executeUpdate();
//需要查找就实现这个接口 ResultSet rs = stmt.executeQuery();
返回布尔类型像迭代器//Rs.next()
Rs.getString(“输入列表名”)
Rs.getDouble(“同上”)
Rs.getint(“同上”)
jdbc核心接口(重要) 面向接口编程
|- Drvier接口: 用于连接数据库
Connection conn = connect() 连接数据库
|- Connection接口: 代表数据库的连接。可以创建不同类型的Statement对象,执行不同类型的sql语句
Statement createStatement() 创建Statement对象
PreparedStatement prepareStatement(String sql) 创建PreparedStatement对象
|- Statement 接口:用于执行*静态 SQL* 语句
int executeUpdate(String sql) 执行更新类的sql语句 (DDL+DML: create/drop/alter/insert/update/delete)
ResultSet executeQuery(String sql) 执行查询类的sql语句(DQL: select/show)
|- PreparedStatement接口:用于执行*预编译的 SQL* 语句
int executeUpdate() 执行更新类的sql语句
ResultSet executeQuery() 执行查询类的sql语句
|-ResultSet接口:用于封装数据库的结果
next() 将光标移动到下一行, true:有数据 false:没有数据
getXXX(索引|字段名称) 取出当前行的列值
把jdbc抽取做成工具类
(路径查找)
读取文件路径问题:
new FileInputStream("绝对路径、相对路径")
绝对路径: c:/ddd/jdbc.properties 有问题,写死了,不通用!
相对路径: ./jdbc.properties 有问题,点 所在的目录会随着java命令的运行目录发生改变
java项目:指向项目目录
web项目:指向tomcat的bin目录
(*)类路径:
InputStream in = JdbcUtil.class.getResourceAsStream("/jdbc.properties");
斜杠(/) 指向项目的类路径
java项目:指向bin目录, src的文件拷贝发到bin目录
web项目:执行WEB-INF/classes目录, src的文件拷贝到WEB-INF/classes目录
总结读取文件的方式:
方式一: new FileInputStream("绝对路径");
方式二: new FileInputStream("相对路径");
方式三: getServletContext().getRealPath("web应用路径"); /images/mm.jpg
局限:一定是web项目
方式四: class.getResourceAsStream("类路径") /jdbc.properties
局限:只能读取类路径下面的文件,如果类路径以外读不到
Jdbc批处理(类似IO流)
Statement批处理: 内部设计一个集合,用于存储sql
addBatch(sql) 将一条sql添加到缓存
executeBatch() 将缓存的所有sql语句发送到数据库
clearBatch() 清空缓存所有sql语句
PreparedStatement批处理
addBatch() 将参数添加到缓存
executeBatch() 将所有参数发送到数据库
clearBatch() 清空缓存所有参数
1)写入数据库:
字符文件: setClob(xxx)
字节文件: setBlob(xxx)
2)从数据库读取文件:
字符文件: getClob(xx)
字节文件: getBlob(xxx)
mysql创建语法
数据库支持文件类型
字符文件:text mediumtext longtext
64kb 16m 4gb
字节文件:blob mediumblob longblob
64kb 16m 4gb
CREATE TABLE 表格名 (file mediumtext //创建一个16M的文件存储格);
事务(就是sql语句)
数据库控制事务
set autocommit=0; 开启事务
commit; 提交事务
rollback 回滚事务
jdbc控制数据库事务(重点)
Connection.setAutoCommit(false) 开启事务
Connection.commit() 提交事务
Connection.rollback() 回滚事务
事务等级
可以设置数据库的隔离级别:(级别越高越安全,但是效率越低,看需求选择2,3)
脏读 不可重复读 幻读
read uncommitted 是 是 是
read committed 否 是 是 (oracle默认)
repeatable read 否 否 是 (mysql默认)
serializable 否 否 否
查看数据库的隔离级别:
SELECT @@global.tx_isolation
设置数据库的隔离级别:
set global transaction isolation level serializable;
事务的四个特性(面试时候) ACID
1)原子性(A): 事务是一个不可隔的工作单元,要么一起成功,要么一起失败!
2)一致性(C): 事务必须使数据库从一个一致性状态变化到另一个一致性状态。
3隔离性(I): 事务必须使多个并发的事务相互隔离
4)持续性/永久性(Durability) 一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。
如果数据库的事务没有隔离性,就可能会产生以下现象;
1)脏读:一个事务读到了另一个事务没有提交的更新数据
2)不可重复读:一个事务读到了另一个事务已经提交的更新数据
3)幻读:一个事务读到了另一个事务*已经提交*的新插入数据
JDBC增强(工具)
代理模式
例如:(Human接口,需要一个SpingBrother类实现,一个代理人也实现了接口,内部无参代码块new一个SpingBrother实现类,就能调用实现类的方法,也能自己添加东西进去)
连接池
为什么用连接池?
目前的连接模式,连接对象的使用率太低,导致每次请求执行的效率低
数据库连接数量得不到控制,导致数据库服务器崩溃
自定义连接池原理
需要用代理模式实现Connection接口,在构造方法里实现,然后重新Connection方法
增强jdbc工具(c3p0,dbcp)
Dbcp用properties配置文件(规范)
6:为初始化连接池个数
7:最大连接池个数
8:最多等待时间(毫秒)
Java代码
C3P0(推荐)
用xml配置可以配置多个数据库
<c3p0-config>
<!-- 默认配置:仅有1个 -->
<default-config>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/day16?useUnicode=true&
;characterEncoding=utf-8</property> //url:mysql地址
<property name="user">root</property> //账号
<property name="password">root</property> //密码
<property name="driverClass">com.mysql.jdbc.Driver</property> //导包
<property name="initialPoolSize">5</property> //初始化连接池
<property name="maxPoolSize">15</property> //最大连接池个数
<property name="checkoutTimeout">3000</property> //最多检验等待时间
</default-config>
<!-- 命名配置:(n个) -->
<named-config name="empsys">
<property name="jdbcUrl">jdbc:mysql://localhost:3306/empsys</property>
<property name="user">root</property>
<property name="password">root</property>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="initialPoolSize">5</property>
<property name="maxPoolSize">20</property>
<property name="checkoutTimeout">3000</property>
</named-config>
</c3p0-config>
Java代码
//c3p0版本的连接工具-配置文件(推荐)
public class C3P0Util2 {
//创建一个连接池对象
private static ComboPooledDataSource ds = new ComboPooledDataSource();//读取默认配置
private static ComboPooledDataSource ds2 = new ComboPooledDataSource("empsys");// 读取命名配置
//返回连接池对象
public static DataSource getDataSource(){
return ds;
}
public static Connection getConnection(){
try {
Connection conn = ds2.getConnection();
return conn;
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
//连接池的关闭是让他返回连接池,以便继续调用
public static void close(Statement stmt,Connection conn) throws Exception{
if(stmt!=null) stmt.close();
if(conn!=null) conn.close();
}
}
作用:为了写出通用的代码
数据库元数据: DatabaseMetaData(数据库名字一类的)
获取数据库相关信息
参数元数据: ParameterMetaData(例如个数)
获取参数相关信息
结果集元数据:ResultSetMetaData(例如列的值)
获取结果集相关信息
Myeclipse中day19和day19_01
Jdbc最终实现简单框架
导入c3p0-0.9.1.2.jar实现注册驱动连接工具
导入mysql-connector-java-5.1.7-bin.jar连接MySQL数据库
***导入commons-dbutils-1.6.jar 封装了SQL语句框架
核心类:QueryRunner(”导入驱动Connection”)
update() 执行更新类操作
query() 执行查询类操作
ResultSetHandler接口:结果集封装对象(在更新与查询里new出这几个对象)
默认实现:
*BeanHandler: 一个javabean
*BeanListHandler: 一个List(包含javabean)
ArrayHandler: 一个对象数组
ArrayListHandler: 一个List(包含对象数组)
(返回longl类型转int类型intValue()方法)ScalarHandler: 一个对象(聚合查询)select count(*) from employee
分页与搜索
主要代码day19和day19_01
过滤器(Filter)
filter: javax.servlet.Filer 拦截请求和响应,执行过滤任务
配置
filter:
<filter>
<filter-name>任意名
<filter-class>包名
</filter>
<filter-mapping>
<filter-name>两个名字一致
<url-pattern>需要过滤的地址
<url-pattern>可以过滤多个
</filter-mapping>
//可以加入权限
<dispatcher>
**<dispatcher>REQUEST</dispatcher>(默认):直接请求的方法过滤器才会起作用
*<dispatcher>FORWARD</dispatcher>:转发的方式过滤器才会起作用
<dispatcher>INCLUDE</dispatcher>: 动态包含的方法过滤器才会起作用
<dispatcher>ERROR</dispatcher>: 错误指向的方法过滤器才会起作用
使用:
建一个普通的类实现Filter接口在重写doFilter方法
注意用
1:chain.doFilter()放行
2: 过滤器链中的执行顺序是由<filter-mapping>的配置顺序决定的,先配置的先执行!!!
装饰者模式
如果遇到某个类的某个(某些)方法不满足需求,希望增强其方法内容,那么这是可以使用装饰者模式。
例如:BufferedReader的readLine()不满足需求,装饰BufferedReader,增强readLine()方法
步骤
1)编写一个装饰类,去装饰被装饰类,继承被装饰类(被装饰类不能是final)如BufferedReader
2)在装饰类中声明一个被装饰类的成员变量 private BufferedReader br;
*3)在装饰类的构造方法中接收被装饰类的实例对象 this.br=传进来的对象
4)增强(重写)被装饰类的装饰方法
装饰者与代理者的区别
public SBProxy implements Human{
private Human human;
public SBProxy (){
this.human=new SpringBrother(); // 代理,不是装饰者
}
public SBProxy (Human sb){
this.human=sb; // 即使代理 ,又是装饰者
}
Web监听器
监听器,是一套接口,javax.servlet.XXXListener
总结:
web监听器:用于监听ServletContext,ServletRequest,HttpSession这三个对象的创建销毁,属性增删改行为。
使用:
在web.xml文件中配置
<listener>
<listener-class>包名
</listener>
事件源 事件 web监听器(接口需要类实现)
(重点)
HttpSession对象 创建或销毁 HttpSessionListener
HttpSession对象 属性增删改 HttpSessionAttributeListener
(了解)
ServletContext对象 创建或销毁 ServletContextListener
ServletContext对象 属性增删改 ServletContextAttributeListene
(了解)
ServletRequest对象 创建或销毁 ServletRequestListener
ServletRequest对象 属性增删改 ServletRequestAttributeListener
HttpSessionListener接口
作用
监听HttpSession对象的创建和销毁
时机
创建:调用request.getSession()方法
销毁:
1)默认30分钟,自动销毁
2)修改时间: setMaxInactiveInterval()
<session-config>
<session-timeout>
</session-config>
3)直接销毁: invalidate()
案例:统计网站的在线访客人数
public class MySessionListener implements HttpSessionListener {
private int onLine;//用于记录当前网站的访问人数
public void sessionCreated(HttpSessionEvent hse) { //创建时
synchronized (MySessionListener.class) { // 使用同步把代码锁住(锁对象需要唯一) 注意:通常使用class对象锁住代码块
onLine++;
//放入context域中
hse.getSession().getServletContext().setAttribute("onLine", onLine);
}
}
public void sessionDestroyed(HttpSessionEvent hse) { //销毁时
synchronized (MySessionListener.class) {
onLine--;
//放入context域中
hse.getSession().getServletContext().setAttribute("onLine", onLine);
}
}
}
HtttpSessionAttributeListener接口
作用
监听HttpSession对象属性的增删改
增:setAttribute("name",Object); 第一次
改:setAttribute("name",Object); 同名第二次 (修改属性)
删:removeAttribute("name")
案例:统计登录用户信息
统计登录用户信息:
1)完成登录功能和注销功能
*2)统计*登录用户*列表: 用户名 登录IP地址 登录时间
public class MySessionAttributeListener implements HttpSessionAttributeListener {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
List<LoginBean> loginUsers = new ArrayList<LoginBean>();
public void attributeAdded(HttpSessionBindingEvent hsbe) {
if(hsbe.getName().equals("loginInfo")){
synchronized (MySessionAttributeListener.class) {
//获取HttpSession
HttpSession session = hsbe.getSession();
//1.把登录用户信息封装到LoginBean
LoginBean lb = new LoginBean();
//session的Id
lb.setSessionID(session.getId());
//用户名
lb.setName((String)session.getAttribute("loginInfo"));
//ip
lb.setIp((String)session.getAttribute("ip"));
//登录时间
lb.setLoginTime(sdf.format(new Date()));
//2.把LoginBean放入List集合
loginUsers.add(lb);
//3.把List集合放入context域
session.getServletContext().setAttribute("loginUsers", loginUsers);
}
}
}
public void attributeRemoved(HttpSessionBindingEvent hsbe) {
/*String name = hsbe.getName();
//属性值
Object value = hsbe.getValue();
System.out.println("属性删除:"+name+"="+value);*/
//判断只要loginInfo
if(hsbe.getName().equals("loginInfo")){
synchronized (MySessionAttributeListener.class) {
//1.从context域中取出List集合
HttpSession session = hsbe.getSession();
List<LoginBean> loginUsers = (List<LoginBean>)session.getServletContext().getAttribute("loginUsers");
//2.删除指定的对象
for(LoginBean lb:loginUsers){
if(lb.getSessionID().equals(session.getId())){
loginUsers.remove(lb);
break;
}
}
//3.保存回context
session.getServletContext().setAttribute("loginUsers", loginUsers);
}
}
}
public void attributeReplaced(HttpSessionBindingEvent hsbe) {
String name = hsbe.getName();
//属性值
Object value = hsbe.getSession().getAttribute(name);
System.out.println("属性修改:"+name+"="+value);
}
}
El表达式国际化
1:配置properties文件,文件名要规范message_(语言简称如ZH)_(国家简称如CN).properties
2:在jsp文件中导入<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
文字国际化
<fmt:setBundle/> 绑定资源文件
<fmt:message/> 读取资源文件的内容
日期时间国际化
<fmt:formatDate value="<%=new Date()%>" dateStyle="medium" type="both" timeStyle="full"/>
货币国际化
<fmt:formatNumber value="0.23" type="percent"></fmt:formatNumber>
文件上传和下载
三个必要条件
1:jsp或html必须有一个form表单,表单里面有一个file组件
2:form表单必须是post提交
3:form表单enctype设置为 ” multipart/form-data” ;
创建Servlet文件,导入jar包
commons-fileupload-1.2.2.jar 核心包
commons-io-2.1.jar 辅助包
核心方法
DiskFileItemFactory类:设置缓存参数
*ServletFileUpload类:核心主类,用于解析文件
List<FileItem> parseRequest(request): 解析请求中的文件
FileItem类:代表的是一个文件信息
文件名:getName()
文件类型:getContentType()
文件内容: getInputStream()
文件大小: getSize()
//1.创建DiskFileItemFactory:设置缓存
DiskFileItemFactory factory = new DiskFileItemFactory(); //
//2.创建ServletFileUpload:解析文件
ServletFileUpload sfu = new ServletFileUpload(factory);
/**
* 解决文件名称中文乱码问题
*/
sfu.setHeaderEncoding("utf-8");
//3.解析文件
try {
List<FileItem> list = sfu.parseRequest(request);
//4.取出文件信息
if(list!=null){
FileItem item = list.get(0); //多个用for遍历list
String fileName = item.getName();
String contentType = item.getContentType();
long size = item.getSize();
InputStream is = item.getInputStream();
System.out.println("文件名称:"+fileName);
System.out.println("文件类型:"+contentType);
System.out.println("文件大小:"+size);
//5.保存到服务器硬盘中
FileUtils.copyInputStreamToFile(is, new File("C:/targetFiles/"+fileName));
}
} catch (FileUploadException e) {
e.printStackTrace();
}
限制上传文件
限制类型
/**
* 限制文件类型
* 只能上传图片 png jpg gif......
* image/xxx
*/
if(!contentType.matches("image/[a-zA-Z-]{3,}")){ //利用正则
request.setAttribute("msg", "不支持该文件类型");
request.getRequestDispatcher("/03.upload.jsp").forward(request, response);
return;
}
限制大小
sfu.setFileSizeMax(200*1024); //限制单个文件大小
sfu.setSizeMax(400*1024); // 限制所有文件大小
解决文件名重复
/**
* 生成唯一的随机文件名
*/
String uuid = UUID.randomUUID().toString();
//后缀
String ext = fileName.substring(fileName.lastIndexOf("."));
//保存的文件名称
String saveFileName = uuid+ext;
解决上传图片加获取表单内容
//判断哪些是普通表单
if(item.isFormField()){
//普通表单
//获取普通表单数据
//获取普通表单的name属性
if(item.getFieldName().equals("userName")){
String name = item.getString("utf-8");
System.out.println("姓名:"+name);
}
if(item.getFieldName().equals("email")){
String name = item.getString("utf-8");
System.out.println("邮箱:"+name);
}
文件下载
//1.读取所需下载的文件
//得到文件所在的绝对路径
String path = getServletContext().getRealPath("/attach/mm.jpg");
//创建文件
File file = new File(path);
//创建输入流
FileInputStream is = new FileInputStream(file);
//2.发送一个响应头:content-disposition.通知浏览器弹出一个下载提示框
response.setHeader("content-disposition", "attachment;filename="+file.getName());
//3.把文件内容输出给浏览器
OutputStream out = response.getOutputStream();
byte[] buf = new byte[1024];
int len = 0;
while( (len=is.read(buf))!=-1 ){
out.write(buf, 0, len);
}
out.close();
is.close();
javaMail技术
发送邮件
smtp协议:发送邮件的协议 simple mail transfer protocol 简单邮件传输协议
代码F:\java就业班\day22_文件上传下载和JavaMail\01.代码\day22_03_mail