古老的技术,jdbc+jsp+servlet。生活就像俄罗斯套娃,一个套着一个。我们后面看着的大框架,像剥洋葱一般一层一层剥下来,底层就是一些技术。虽然有的过时,但是从中可以窥见一丝前人的思想。
xml XML XML介绍 什么是XML
W3C在1988年2月发布1.0版本,2004年2月又发布1.1版本,但因为1.1版本不能向下兼容1.0版本,所以1.1没有人用。同时,在2004年2月W3C又发布了1.0版本的第三版。我们要学习的还是1.0版本。
XML 与 HTML 的主要差异
html语法松散,xml语法严格,区分大小写
html做页面展示,xml传输数据
html所有标签都是预定义的,xml所有标签都是自定义的
xml的作用
XML组成元素-重点 一个标准XML文件一般由以下几部分组成:文档声明、元素、属性、注释、转义字符、字符区。
文档声明 1 <?xml version="1.0" encoding="utf-8" ?>
文档声明可以不写
文档声明必须为<?xml开头,以?>结束
文档声明必须从文档的0行0列位置开始
文档声明中常见的两个属性:
version:指定XML文档版本。必须属性,这里一般选择1.0;
enconding:指定当前文档的编码,可选属性,默认值是utf-8;
注释
XML的注释,既以<!--
开始,-->
结束。
注释不能嵌套
idea上快捷键: ctrl + /
元素\标签
元素是XML中最重要的组成部分,元素也叫标签
标签分为开始标签和结束标签,开始标签<名字> 结束标签</名字>
开始标签和结束标签中间写的是标签内容,标签的内容可以是文本,也可以是其他标签
如果标签没有任何内容,那么可以定义空标签(比如:<名字/>)
标签可以嵌套,但是不能乱嵌套
一个XML文件只有一个根标签
命名规则:
不要使用XML xML xml 这样的单词
不能使用空格,冒号
命名区分大小写
数字不能开头
1 2 3 4 5 6 <?xml version="1.0" encoding="UTF-8" ?> <person > <name > 唐三</name > <age > 年龄</age > <aaa /> </person >
属性
位置: 属性是元素的一部分,它必须出现在元素的开始标签中,不能写在结束标签中
格式: 属性的定义格式:属性名=”属性值”,其中属性值必须使用单引或双引号括起来
一个元素可以有0~N个属性,但一个元素中不能出现同名属性
属性名必须符合标识符命名规范和规则
1 2 3 4 5 6 7 <?xml version="1.0" encoding="UTF-8" ?> <person > <name id = "001" level = '98' > 唐三</name > <age > 10</age > <aaa type = 'itheima' /> </person >
转义字符 因为有些特殊的字符在XML中是不会被识别的,所以在元素体或属性值中想使用这些符号就必须使用转义字符(也叫实体字符),例如:>
、<
、'
、"
,&
。
注意:严格地讲,在 XML 中仅有字符 “<”和”&” 是非法的 。省略号、引号和大于号是合法的,但是把它们替换为实体引用是个好的习惯。
转义字符应用示例:
1 <price > 苹果的价格: price > 5 & & price < 10</price >
字符区(了解)
CDATA 内部的所有东西都会被解析器忽略,当做文本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <![CDATA[ 文本数据 ]]> 快捷键:CD <> <!> <![]> <![CDATA]> <![CDATA[ 文本 ]]> <price > <![CDATA[ 苹果的价格: price > 5 && price < 10 ]]> </price >
XML文件的约束-DTD约束(了解) xml约束概述
在XML技术里,可以编写一个文档来约束一个XML文档的书写规范,这称之为XML约束。
约束文档定义了在XML中允许出现的元素(标签)名称、属性及元素(标签)出现的顺序等等。
两种约束:DTD约束(文件后缀为dtd),Schema约束(文件后缀为xsd)
注意: 约束不是我们要写的东西,我们的工作是根据约束去写XML
根据DTD约束写XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 <?xml version="1.0" encoding="UTF-8" ?> <!ELEMENT 书架 (书+)> <!ELEMENT 书 (书名,作者,售价)> <!ELEMENT 书名 (#PCDATA )> <!ELEMENT 作者 (#PCDATA )> <!ELEMENT 售价 (#PCDATA )> <!ATTLIST 书 id ID #REQUIRED 编号 CDATA #IMPLIED 出版社 (清华|北大|传智播客) "传智播客" type CDATA #FIXED "IT" >
XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE 书架 SYSTEM "bookdtd.dtd" > <书架 > <书 id ="a1" 编号 ="001" 出版社 ="清华" type ="IT" > <书名 > 斗罗大陆</书名 > <作者 > 唐家三少</作者 > <售价 > 99.8</售价 > </书 > <书 id ="a2" > <书名 > java从入门到放弃</书名 > <作者 > 无名氏</作者 > <售价 > 9.8</售价 > </书 > </书架 >
语法(了解) 文档声明(了解)
内部DTD,在XML文档内部嵌入DTD,只对当前XML有效。
1 2 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE 根元素 [元素声明]> >
外部DTD—本地DTD,DTD文档在本地系统上,企业内部自己项目使用。
1 2 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE 根元素 SYSTEM "文件名" >
外部DTD—公共DTD,DTD文档在网络上,一般都有框架提供 , 也是我们使用最多的.
1 2 3 4 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE 根元素 PUBLIC "DTD名称" "DTD文档的URL" > 例如: <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" >
元素声明(了解)
约束元素的嵌套层级
语法
1 2 3 4 <!ELEMENT 父标签 (子标签1,子标签2,…)> 例如: <!ELEMENT books (book +)> <!ELEMENT book (name ,author ,price )>
约束元素体里面的数据
语法
1 2 <!ELEMENT 标签名字 标签类型> 例如 <!ELEMENT name (#PCDATA )>
标签类型: EMPTY(即空元素,例如<hr/>) ANY(任意类型) (#PCDATA) 字符串数据
代码
1 2 3 <!ELEMENT name (#PCDATA)> <!ELEMENT author (#PCDATA)> <!ELEMENT price (#PCDATA)>
数量词(掌握)
数量词符号
含义
*
表示元素可以出现0到多个
+
表示元素可以出现至少1个
?
表示元素可以是0或1个
,
表示元素需要按照顺序显示
|
表示元素需要选择其中的某一个
属性声明(了解) 语法
1 2 3 4 5 6 7 <!ATTLIST 标签名称 属性名称1 属性类型1 属性说明1 属性名称2 属性类型2 属性说明2 … > 例如 <!ATTLIST book bid ID #REQUIRED>
属性类型
CDATA :表示文本字符串
ID:表示属性值唯一,不能以数字开头
ENUMERATED (DTD没有此关键字):表示枚举,只能从枚举列表中任选其一,如(鸡肉|牛肉|猪肉|鱼肉)
属性说明:
REQUIRED:表示该属性必须出现
IMPLIED:表示该属性可有可无
FIXED:表示属性的取值为一个固定值。语法:#FIXED "固定值"
属性说明
代码
1 2 3 4 5 6 <!ATTLIST 书 <!--设置"书"元素的的属性列表--> id ID #REQUIRED <!--"id"属性值为必须有--> 编号 CDATA #IMPLIED <!--"编号"属性可有可无--> 出版社 (清华|北大|传智播客) "传智播客" <!--"出版社"属性值是枚举值,默认为“传智播客”--> type CDATA #FIXED "IT" <!--"type"属性为文本字符串并且固定值为"IT"--> >
案例 1 2 3 4 5 6 7 8 9 10 11 12 13 <?xml version = "1.0" encoding="GB2312" standalone="yes" ?> <!DOCTYPE 购物篮 [ <!ELEMENT 购物篮 (肉+)> <!ELEMENT 肉 EMPTY > <!ATTLIST 肉 品种 ( 鸡肉 | 牛肉 | 猪肉 | 鱼肉 ) "鸡肉" > ]> <购物篮 > <肉 品种 ="牛肉" > </肉 > <肉 品种 ="牛肉" > </肉 > <肉 品种 ="鱼肉" > </肉 > <肉 /> </购物篮 >
schema约束(了解) 概念 schema和DTD一样, 也是一种XML文件的约束.
Schema 语言也可作为 XSD(XML Schema Definition)。
Schema约束的文件的后缀名.xsd
Schema 功能更强大,数据类型约束更完善。
根据schema约束写出xml文档
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 <?xml version="1.0" encoding="UTF-8" ?> <xs:schema xmlns:xs ="http://www.w3.org/2001/XMLSchema" targetNamespace ="http://www.itcast.cn" elementFormDefault ="qualified" > <xs:element name ='书架' > <xs:complexType > <xs:sequence maxOccurs ="2" minOccurs ="1" > <xs:element name ='书' > <xs:complexType > <xs:sequence > <xs:element name ='书名' type ='xs:string' /> <xs:element name ='作者' type ='xs:string' /> <xs:element name ='售价' type ='xs:double' /> </xs:sequence > <xs:attribute name ="bid" type ="xs:int" use ="optional" /> </xs:complexType > </xs:element > </xs:sequence > </xs:complexType > </xs:element > </xs:schema >
根据上面的Schema约束编写XML
声明方式
1 2 3 4 <书架 xmlns="http://www.itcast.cn" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.itcast.cn bookSchema.xsd" >
案例1
1 2 3 4 5 6 7 8 9 10 <?xml version="1.0" encoding="UTF-8" ?> <书架 xmlns ="http://www.itcast.cn" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://www.itcast.cn bookSchema.xsd" > <书 bid ="1" > <书名 > 斗罗大陆</书名 > <作者 > 唐家三少</作者 > <售价 > 99.8</售价 > </书 > </书架 >
案例2
1 2 3 4 5 6 7 8 9 10 <?xml version="1.0" encoding="UTF-8" ?> <a:书架 xmlns:a ="http://www.itcast.cn" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://www.itcast.cn bookSchema.xsd" > <a:书 bid ="1" > <a:书名 > 斗罗大陆</a:书名 > <a:作者 > 唐家三少</a:作者 > <a:售价 > 99.8</a:售价 > </a:书 > </a:书架 >
Dom4j XML解析 解析方式
解析包
JAXP:sun公司提供支持DOM和SAX开发包
Dom4j: 比较简单的的解析开发包(常用),
JDom:与Dom4j类似
Jsoup:功能强大DOM方式的XML解析开发包,尤其对HTML解析更加方便(项目中讲解)
Dom4j的基本使用
为什么了解它? 很多框架底层多是用dom4j解析xml的
DOM解析原理及结构模型
使用步骤
导入jar包 dom4j-1.6.1j.jar
创建解析器
读取xml 获得document对象
得到根元素
根据根元素获取对于的子元素或者属性
常用的方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 创建解析器对象: SAXReader sr = new SAXReader (); 解析器读取文件方法: Document doc = sr.read(String fileName); Document的方法: getRootElement() : 获取根元素 节点中的方法: elements() : 获取当前元素的子元素 element(String name) : 根据元素名获取指定子元素(如果有多个就获取到第一个) getName() : 获取元素的元素名 elementText(String name) : 获取指定子元素的文本值,参数是子元素名称 attributeValue(String name) : 获取当前元素下某个属性的值 getText() : 获取当前元素的文本值
方法演示
xml
1 2 3 4 5 6 7 8 9 10 11 12 13 <?xml version="1.0" encoding="UTF-8" ?> <books > <book id ="0001" > <name > JavaWeb开发教程</name > <author > 张孝祥</author > <sale > 100.00元</sale > </book > <book id ="0002" > <name > 三国演义</name > <author > 罗贯中</author > <sale > 100.00元</sale > </book > </books >
解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 package com.itheima.demo1_Dom4j的基本使用;import org.dom4j.Document;import org.dom4j.Element;import org.dom4j.io.SAXReader;import java.util.List;public class Test1 { public static void main (String[] args) throws Exception { SAXReader sr = new SAXReader (); Document document = sr.read("day16\\src\\book.xml" ); Element rootE = document.getRootElement(); System.out.println("根元素的名称:" + rootE.getName()); List<Element> list1 = rootE.elements(); for (Element e1 : list1) { System.out.println("根元素的子元素的名称:" + e1.getName()); System.out.println("根元素的子元素的id属性值:" + e1.attributeValue("id" )); List<Element> list2 = e1.elements(); for (Element e2 : list2) { System.out.println("book下的子元素名:" + e2.getName()); System.out.println("book下的子元素中的文本:" + e2.getText()); } System.out.println("==================================" ); } Element bookE = rootE.element("book" ); System.out.println(bookE.getName()); System.out.println(bookE.attributeValue("id" )); String text = bookE.elementText("author" ); System.out.println(text); } }
Dom4J结合XPath解析XML 介绍 XPath 使用路径表达式来选取HTML\XML
文档中的元素节点或属性节点。节点是通过沿着路径 (path) 来选取的。XPath在解析HTML\XML
文档方面提供了独树一帜的路径思想。
XPath使用步骤 步骤1:导入jar包(dom4j和jaxen-1.1-beta-6.jar)
步骤2:通过dom4j的SaxReader解析器对象,获取Document对象
步骤3: 利用Xpath提供的api,结合xpaht的语法完成选取XML文档元素节点进行解析操作。
document常用的api
document.selectSingleNode("xpath语法");
获得一个节点(标签,元素)
document.selectNodes("xpath语法");
获得多个节点(标签,元素)
XPath语法(了解)
绝对路径表达式(了解) 绝对路径介绍: 以/开头的路径叫做是绝对路径,绝对路径要从根元素开始写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class Test { public static void main (String[] args) throws Exception { SAXReader sr = new SAXReader (); Document document = sr.read("day22/src/tianqi.xml" ); Element rootE = document.getRootElement(); System.out.println(rootE); Node node = rootE.selectSingleNode("/天气预报/深圳/湿度" ); System.out.println(node.getName()); System.out.println(node.getText()); } }
相对路径表达式(了解) 相对路径介绍: 相对路径就是相对当前节点元素位置继续查找节点,不以/
开头, ../
表示上一个元素, ./
表示当前元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public class Test { public static void main (String[] args) throws Exception { SAXReader sr = new SAXReader (); Document document = sr.read("day22/src/tianqi.xml" ); Element rootE = document.getRootElement(); System.out.println(rootE); Node node = rootE.selectSingleNode("./深圳/湿度" ); System.out.println(node.getName()); System.out.println(node.getText()); Node node2 = document.selectSingleNode("/天气预报/北京/温度" ); Node node3 = node2.selectSingleNode("../湿度" ); System.out.println(node3.getName()); System.out.println(node3.getText()); } }
全文搜索路径表达式(了解) 全文搜索路径介绍: 代表不论中间有多少层,直接获取所有子元素中满足条件的元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class Test { public static void main (String[] args) throws Exception { SAXReader sr = new SAXReader (); Document document = sr.read("day22/src/tianqi.xml" ); Element rootE = document.getRootElement(); System.out.println(rootE); List<Element> list = rootE.selectNodes("//湿度" ); for (Element e : list) { System.out.println(e.getName()+"," +e.getText()); } } }
谓语(条件筛选 了解) 介绍: 谓语,又称为条件筛选方式,就是根据条件过滤判断进行选取节点
格式:
1 2 3 4 5 String xpath1="//元素[@attr1=value]" ; String xpath2="//元素[@attr1>value]/@attr1" String xpath3="//元素[@attr1=value]/text()" ; String xpath4="//元素[@attr1=value]/html()" ; String xpath3="//元素[@attr1=value]/allText()" ;
演示 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class Test { public static void main (String[] args) throws Exception { SAXReader sr = new SAXReader (); Document document = sr.read("day22/src/tianqi.xml" ); Element rootE = document.getRootElement(); System.out.println(rootE); List<Element> list = rootE.selectNodes("//最高温度[@level='C']" ); for (Element e : list) { System.out.println(e.getName()+"," +e.getText()); } } }
正则表达式 正则表达式的概念及演示 概述: 正则表达式其实就是一个匹配规则,用来替换之前复杂的if结构判断
在Java中,我们经常需要验证一些字符串,是否符合规则, 例如:校验qq号码是否正确,手机号码是否正确,邮箱是否正确等等。那么如果使用if就会很麻烦, 而正则表达式就是用来验证各种字符串的规则。它内部描述了一些规则,我们可以验证用户输入的字符串是否匹配这个规则。
先看一个不使用正则表达式验证的例子:下面的程序让用户输入一个QQ号码,我们要验证:
QQ号码必须是5–15位长度
而且必须全部是数字
而且首位不能为0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public class Demo { public static void main (String[] args) { Scanner sc = new Scanner (System.in); System.out.println("请输入你的QQ号码:" ); String qq = sc.next(); System.out.println(checkQQ(qq)); } private static boolean checkQQ (String qq) { if (qq.length() < 5 || qq.length() > 15 ){ return false ; } for (int i = 0 ;i < qq.length() ; i++){ char c = qq.charAt(i); if (c < '0' || c > '9' ){ return false ; } } char c = qq.charAt(0 ); if (c == '0' ){ return false ; } return true ; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class Demo { public static void main (String[] args) { Scanner sc = new Scanner (System.in); System.out.println("请输入你的QQ号码:" ); String qq = sc.next(); System.out.println(checkQQ2(qq)); } private static boolean checkQQ2 (String qq) { String regex = "[1-9]\\d{4,14}" ; return qq.matches(regex); } }
上面程序checkQQ2()方法中String类型的变量regex就存储了一个”正则表达式 “,而这个正则表达式就描述了我们需要的三个规则。matches()方法是String类的一个方法,用于接收一个正则表达式,并将”本对象”与参数”正则表达式”进行匹配,如果本对象符合正则表达式的规则,则返回true,否则返回false。
正则表达式的基本使用 正则表达式-字符类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public class Test1 { public static void main (String[] args) { String str = "ead" ; System.out.println(str.matches("h[aeiou]d" )); System.out.println("hed" .matches("h[aeiou]d" )); System.out.println("heod" .matches("h[aeiou]d" )); System.out.println("==========================================" ); System.out.println(str.matches("h[^aeiou]d" )); System.out.println("hed" .matches("h[^aeiou]d" )); System.out.println("hbd" .matches("h[^aeiou]d" )); System.out.println("==========================================" ); System.out.println(str.matches("[a-z]ad" )); System.out.println("Aad" .matches("[a-z]ad" )); System.out.println("==========================================" ); System.out.println(str.matches("[a-dm-p]ad" )); System.out.println("bad" .matches("[a-dm-p]ad" )); System.out.println("nad" .matches("[a-dm-p]ad" )); } }
正则表达式-逻辑运算符
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public class Test2 { public static void main (String[] args) { String str = "had" ; System.out.println(str.matches("[a-z&&[^aeiou]]ad" )); System.out.println("aad" .matches("[a-z&&[^aeiou]]ad" )); System.out.println("Aad" .matches("[a-z&&[^aeiou]]ad" )); System.out.println("========================" ); System.out.println(str.matches("[aeiou]ad" )); System.out.println(str.matches("[a|e|i|o|u]ad" )); System.out.println("aad" .matches("[aeiou]ad" )); System.out.println("aad" .matches("[a|e|i|o|u]ad" )); } }
正则表达式-预定义字符
语法示例:
“.” : 匹配任何字符。如果要表示一个字符点,那么就得使用\\.
“\d”:任何数字[0-9]
的简写;
“\D”:任何非数字[^0-9]
的简写;
“\s”: 空白字符:[ \t\n\x0B\f\r]
的简写
“\S”: 非空白字符:[^\s]
的简写
“\w”:单词字符:[a-zA-Z_0-9]
的简写
“\W”:非单词字符:[^\w]
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public class Test3 { public static void main (String[] args) { String str = "258" ; System.out.println(str.matches("\\d\\d\\d" )); System.out.println("25" .matches("\\d\\d\\d" )); System.out.println("+===================================" ); System.out.println(str.matches("1[358]\\d\\d\\d\\d\\d\\d\\d\\d\\d" )); System.out.println("13838381234" .matches("1[358]\\d\\d\\d\\d\\d\\d\\d\\d\\d" )); System.out.println("14838381234" .matches("1[358]\\d\\d\\d\\d\\d\\d\\d\\d\\d" )); System.out.println("+===================================" ); System.out.println(str.matches("h.d" )); System.out.println("h%d" .matches("h.d" )); System.out.println("h#d" .matches("h.d" )); System.out.println("+===================================" ); System.out.println("h.d" .matches("h.d" )); System.out.println("had" .matches("h.d" )); System.out.println("h.d" .matches("h\\.d" )); System.out.println("had" .matches("h\\.d" )); } }
正则表达式-数量词
语法示例:
X? : 0次或1次
X* : 0次到多次
X+ : 1次或多次
X{n} : 恰好n次
X{n,} : 至少n次
X{n,m}: n到m次(n和m都是包含的)
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 public class Test4 { public static void main (String[] args) { System.out.println("123" .matches("\\d{3}" )); System.out.println("12" .matches("\\d{3}" )); System.out.println("1234" .matches("\\d{3}" )); System.out.println("===================================" ); System.out.println("12" .matches("\\d{2,3}" )); System.out.println("123" .matches("\\d{2,3}" )); System.out.println("1223" .matches("\\d{2,3}" )); System.out.println("1" .matches("\\d+" )); System.out.println("12" .matches("\\d+" )); System.out.println("123" .matches("\\d+" )); System.out.println("1234" .matches("\\d+" )); System.out.println("===================================" ); System.out.println("13838381234" .matches("1[358]\\d{9}" )); System.out.println("14838381234" .matches("1[358]\\d{9}" )); System.out.println("===================================" ); System.out.println("123456" .matches("[1-9]\\d{4,14}" )); System.out.println("023456" .matches("[1-9]\\d{4,14}" )); System.out.println("123" .matches("[1-9]\\d{4,14}" )); } }
正则表达式-分组括号( ) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public class Test5 { public static void main (String[] args) { String str = "DG8FV-B9TKY-FRT9J-99899-XPQ4G" ; System.out.println(str.matches("([A-Z0-9]{5}-){4}[A-Z0-9]{5}" )); System.out.println("哈哈" .matches("(.)\\1" )); System.out.println("呵呵" .matches("(.)\\1" )); System.out.println("嘿嘿" .matches("(.)\\1" )); System.out.println("嘿哈" .matches("(.)\\1" )); System.out.println("哈哈哈哈" .matches("(.)\\1{3}" )); System.out.println("呵呵呵呵" .matches("(.)\\1{3}" )); System.out.println("嘿嘿嘿嘿" .matches("(.)\\1{3}" )); System.out.println("===================================" ); System.out.println("高高兴兴" .matches("(.)\\1(.)\\2" )); System.out.println("逼逼赖赖" .matches("(.)\\1(.)\\2" )); System.out.println("===================================" ); System.out.println("快乐快乐" .matches("(..)\\1" )); System.out.println("哔哩哔哩" .matches("(..)\\1" )); } }
String中正则表达式的使用 String的split方法中使用正则表达式
1 public String[] split(String regex)
1 2 3 4 5 6 7 8 9 10 public class Demo { public static void main (String[] args) { String str = "18 4 567 99 56" ; String[] strArray = str.split(" +" ); for (int i = 0 ; i < strArray.length; i++) { System.out.println(strArray[i]); } } }
String类的replaceAll方法中使用正则表达式
String类的replaceAll()方法原型:
1 public String replaceAll (String regex,String newStr)
1 2 3 4 5 6 7 public class Demo { public static void main (String[] args) { String str = "jfdk432jfdk2jk24354j47jk5l31324" ; System.out.println(str.replaceAll("\\d+" , "*" )); } }
总结 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 必须练习: 1. 编写xml文档------元素\属性的书写 2. 使用Dom4j结合xpath路径解析xml文件中的数据 3. 要求能看懂正则表达式 - 能够说出XML的作用 1. 作为配置文件 2. 存储数据 - 了解XML的组成元素 文档声明 元素 属性 注释 转义字符 字符区 - 能够说出有哪些XML约束技术 dtd\xsd - 能够说出解析XML文档DOM方式原理 使用解析器读取xml文档到内存中,加载成Dom树,生成Document对象 - 能够使用dom4j解析XML文档 1. 导入jar包 2. 创建解析器对象 3. 读取xml文档,生成Document对象 4. 使用Document对象获取根元素 5. 根据根元素获取子元素\属性 ... - 能够使用xpath解析XML 1. 导入jar包(2 个) 2. 创建解析器对象 3. 读取xml文档,生成Document对象 4. 使用Document对象调用方法 5. 使用元素调用方法 ... - document.selectSingleNode("xpath语法" ); 获得一个节点(标签,元素) - document.selectNodes("xpath语法" ); 获得多个节点(标签,元素) xpath路径语法: 1. 绝对路径表达式方式 例如: /元素/子元素/子子元素... 以/开头的路径叫做是绝对路径,绝对路径要从根元素开始写 2. 相对路径表达式方式 例如: 子元素/子子元素.. 或者 ./子元素/子子元素.. 相对路径就是相对当前节点元素位置继续查找节点,不以/开头, ../ 表示上一个元素, ./表示当前元素 3. 全文搜索路径表达式方式 例如: 代表不论中间有多少层,直接获取所有子元素中满足条件的元素 4. 谓语(条件筛选)方式 例如: - 能够理解正则表达式的作用 替换复杂的if 结构判断 - 能够使用正则表达式的字符类 语法示例:[] 表示匹配单个字符 ^ 取反 - 范围 1. [abc]:代表a或者b,或者c字符中的一个。 2. [^abc]:代表除a,b,c以外的任何字符。 3. [a-z]:代表a-z的所有小写字符中的一个。 左右包含 4. [A-Z]:代表A-Z的所有大写字符中的一个。 5. [0 -9 ]:代表0 -9 之间的某一个数字字符。 6. [a-zA-Z0-9 ]:代表a-z或者A-Z或者0 -9 之间的任意一个字符。 7. [a-dm-p]:a 到 d 或 m 到 p之间的任意一个字符。 - 能够使用正则表达式的逻辑运算符 && | - 能够使用正则表达式的预定义字符类 1. "." : 匹配任何字符。如果要表示一个字符点,那么就得使用\. 2. "\d" :任何数字[0 -9 ]的简写; 3. "\D" :任何非数字[^0 -9 ]的简写; 4. "\s" : 空白字符:[ \t\n\x0B\f\r] 的简写 5. "\S" : 非空白字符:[^\s] 的简写 6. "\w" :单词字符:[a-zA-Z_0-9 ]的简写 7. "\W" :非单词字符:[^\w] - 能够使用正则表达式的数量词 1. X? : 0 次或1 次 2. X* : 0 次到多次 3. X+ : 1 次或多次 4. X{n} : 恰好n次 5. X{n,} : 至少n次 6. X{n,m}: n到m次(n和m都是包含的) - 能够使用正则表达式的分组 () - 能够在String的split方法中使用正则表达式 略
正则表达式 案例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 正则表达式 正则表达式(英文:Regular Expression)在计算机科学中,是指一个用来描述或者匹配一系列符合某个句法规则的字符串的单个字符串。 常用的正则表达式 常用正则表达式 正则表达式用于字符串处理、表单验证等场合,实用高效。现将一些常用的表达式收集于此,以备不时之需。 用户名:/^[a-z0-9_-]{3,16}$/ 密码:/^[a-z0-9_-]{6,18}$/ 十六进制值:/^#?([a-f0-9]{6}|[a-f0-9]{3})$/ 电子邮箱:/^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/ URL:/^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/ IP 地址:/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/ HTML 标签:/^<([a-z]+)([^<]+)*(?:>(.*)<\/\1>|\s+\/>)$/ Unicode编码中的汉字范围:/^[u4e00-u9fa5],{0,}$/ 匹配中文字符的正则表达式: [\u4e00-\u9fa5] 评注:匹配中文还真是个头疼的事,有了这个表达式就好办了 匹配双字节字符(包括汉字在内):[^\x00-\xff] 评注:可以用来计算字符串的长度(一个双字节字符长度计2,ASCII字符计1) 匹配空白行的正则表达式:\n\s*\r 评注:可以用来删除空白行 匹配HTML标记的正则表达式:<(\S*?)[^>]*>.*?</\1>|<.*? /> 评注:网上流传的版本太糟糕,上面这个也仅仅能匹配部分,对于复杂的嵌套标记依旧无能为力 匹配首尾空白字符的正则表达式:^\s*|\s*$ 评注:可以用来删除行首行尾的空白字符(包括空格、制表符、换页符等等),非常有用的表达式 匹配Email地址的正则表达式:\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)* 评注:表单验证时很实用 匹配网址URL的正则表达式:[a-zA-z]+://[^\s]* 评注:网上流传的版本功能很有限,上面这个基本可以满足需求 匹配帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线):^[a-zA-Z][a-zA-Z0-9_]{4,15}$ 评注:表单验证时很实用 匹配国内电话号码:\d{3}-\d{8}|\d{4}-\d{7} 评注:匹配形式如 0511-4405222 或 021-87888822 匹配腾讯QQ号:[1-9][0-9]{4,} 评注:腾讯QQ号从10000开始 匹配中国大陆邮政编码:[1-9]\d{5}(?!\d) 评注:中国大陆邮政编码为6位数字 匹配身份证:\d{15}|\d{18} 评注:中国大陆的身份证为15位或18位 匹配ip地址:\d+\.\d+\.\d+\.\d+ 评注:提取ip地址时有用 匹配特定数字: ^[1-9]\d*$ //匹配正整数 ^-[1-9]\d*$ //匹配负整数 ^-?[1-9]\d*$ //匹配整数 ^[1-9]\d*|0$ //匹配非负整数(正整数 + 0) ^-[1-9]\d*|0$ //匹配非正整数(负整数 + 0) ^[1-9]\d*\.\d*|0\.\d*[1-9]\d*$ //匹配正浮点数 ^-([1-9]\d*\.\d*|0\.\d*[1-9]\d*)$ //匹配负浮点数 ^-?([1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0)$ //匹配浮点数 ^[1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0$ //匹配非负浮点数(正浮点数 + 0) ^(-([1-9]\d*\.\d*|0\.\d*[1-9]\d*))|0?\.0+|0$ //匹配非正浮点数(负浮点数 + 0) 评注:处理大量数据时有用,具体应用时注意修正 匹配特定字符串: ^[A-Za-z]+$ //匹配由26个英文字母组成的字符串 ^[A-Z]+$ //匹配由26个英文字母的大写组成的字符串 ^[a-z]+$ //匹配由26个英文字母的小写组成的字符串 ^[A-Za-z0-9]+$ //匹配由数字和26个英文字母组成的字符串 ^\w+$ //匹配由数字、26个英文字母或者下划线组成的字符串 表达式全集 正则表达式有多种不同的风格。下表是在PCRE中元字符及其在正则表达式上下文中的行为的一个完整列表: 字符 描述 \ 将下一个字符标记为一个特殊字符、或一个原义字符、或一个向后引用、或一个八进制转义符。例如,“n”匹配字符“n”。“\n”匹配一个换行符。序列“\\”匹配“\”而“\(”则匹配“(”。 ^ 匹配输入字符串的开始位置。如果设置了RegExp对象的Multiline属性,^也匹配“\n”或“\r”之后的位置。 $ 匹配输入字符串的结束位置。如果设置了RegExp对象的Multiline属性,$也匹配“\n”或“\r”之前的位置。 * 匹配前面的子表达式零次或多次。例如,zo*能匹配“z”以及“zoo”。*等价于{0,}。 + 匹配前面的子表达式一次或多次。例如,“zo+”能匹配“zo”以及“zoo”,但不能匹配“z”。+等价于{1,}。 ? 匹配前面的子表达式零次或一次。例如,“do(es)?”可以匹配“do”或“does”中的“do”。?等价于{0,1}。 {n} n是一个非负整数。匹配确定的n次。例如,“o{2}”不能匹配“Bob”中的“o”,但是能匹配“food”中的两个o。 {n,} n是一个非负整数。至少匹配n次。例如,“o{2,}”不能匹配“Bob”中的“o”,但能匹配“foooood”中的所有o。“o{1,}”等价于“o+”。“o{0,}”则等价于“o*”。 {n,m} m和n均为非负整数,其中n<=m。最少匹配n次且最多匹配m次。例如,“o{1,3}”将匹配“fooooood”中的前三个o。“o{0,1}”等价于“o?”。请注意在逗号和两个数之间不能有空格。 ? 当该字符紧跟在任何一个其他限制符(*,+,?,{n},{n,},{n,m})后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串“oooo”,“o+?”将匹配单个“o”,而“o+”将匹配所有“o”。 . 匹配除“\n”之外的任何单个字符。要匹配包括“\n”在内的任何字符,请使用像“[.\n]”的模式。 (pattern) 匹配pattern并获取这一匹配。所获取的匹配可以从产生的Matches集合得到,在VBScript中使用SubMatches集合,在JScript中则使用$0…$9属性。要匹配圆括号字符,请使用“\(”或“\)”。 (?:pattern) 匹配pattern但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用。这在使用或字符“(|)”来组合一个模式的各个部分是很有用。例如“industr(?:y|ies)”就是一个比“industry|industries”更简略的表达式。 (?=pattern) 正向预查,在任何匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如,“Windows(?=95|98|NT|2000)”能匹配“Windows2000”中的“Windows”,但不能匹配“Windows3.1”中的“Windows”。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。 (?!pattern) 负向预查,在任何不匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如“Windows(?!95|98|NT|2000)”能匹配“Windows3.1”中的“Windows”,但不能匹配“Windows2000”中的“Windows”。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始 x|y 匹配x或y。例如,“z|food”能匹配“z”或“food”。“(z|f)ood”则匹配“zood”或“food”。 [xyz] 字符集合。匹配所包含的任意一个字符。例如,“[abc]”可以匹配“plain”中的“a”。 [^xyz] 负值字符集合。匹配未包含的任意字符。例如,“[^abc]”可以匹配“plain”中的“p”。 [a-z] 字符范围。匹配指定范围内的任意字符。例如,“[a-z]”可以匹配“a”到“z”范围内的任意小写字母字符。 [^a-z] 负值字符范围。匹配任何不在指定范围内的任意字符。例如,“[^a-z]”可以匹配任何不在“a”到“z”范围内的任意字符。 \b 匹配一个单词边界,也就是指单词和空格间的位置。例如,“er\b”可以匹配“never”中的“er”,但不能匹配“verb”中的“er”。 \B 匹配非单词边界。“er\B”能匹配“verb”中的“er”,但不能匹配“never”中的“er”。 \cx 匹配由x指明的控制字符。例如,\cM匹配一个Control-M或回车符。x的值必须为A-Z或a-z之一。否则,将c视为一个原义的“c”字符。 \d 匹配一个数字字符。等价于[0-9]。 \D 匹配一个非数字字符。等价于[^0-9]。 \f 匹配一个换页符。等价于\x0c和\cL。 \n 匹配一个换行符。等价于\x0a和\cJ。 \r 匹配一个回车符。等价于\x0d和\cM。 \s 匹配任何空白字符,包括空格、制表符、换页符等等。等价于[\f\n\r\t\v]。 \S 匹配任何非空白字符。等价于[^\f\n\r\t\v]。 \t 匹配一个制表符。等价于\x09和\cI。 \v 匹配一个垂直制表符。等价于\x0b和\cK。 \w 匹配包括下划线的任何单词字符。等价于“[A-Za-z0-9_]”。 \W 匹配任何非单词字符。等价于“[^A-Za-z0-9_]”。 \xnn 匹配n,其中n为十六进制转义值。十六进制转义值必须为确定的两个数字长。例如,“\x41”匹配“A”。“\x041”则等价于“\x04&1”。正则表达式中可以使用ASCII编码。. \num 匹配num,其中num是一个正整数。对所获取的匹配的引用。例如,“(.)\1”匹配两个连续的相同字符。 \n 标识一个八进制转义值或一个向后引用。如果\n之前至少n个获取的子表达式,则n为向后引用。否则,如果n为八进制数字(0-7),则n为一个八进制转义值。 \nm 标识一个八进制转义值或一个向后引用。如果\nm之前至少有nm个获得子表达式,则nm为向后引用。如果\nm之前至少有n个获取,则n为一个后跟文字m的向后引用。如果前面的条件都不满足,若n和m均为八进制数字(0-7),则\nm将匹配八进制转义值nm。 \nml 如果n为八进制数字(0-3),且m和l均为八进制数字(0-7),则匹配八进制转义值nml。 \unnnn 匹配n,其中n是一个用四个十六进制数字表示的Unicode字符。例如,\u00A9匹配版权符号(?)。 以下是以PHP的语法所写的示例 验证字符串是否只含数字与英文,字符串长度并在4~16个字符之间 <?php $str = 'a1234'; if (preg_match("^[a-zA-Z0-9]{4,16}$", $str)) { echo "驗證成功"; } else { echo "驗證失敗"; } ?> 简易的台湾身份证字号验证 <?php $str = 'a1234'; if (preg_match("/^\w[12]\d{8}$/", $str)) { echo "驗證成功"; } else { echo "驗證失敗"; } ?> 以下示例是用 Perl 语言写的,与上面的示例功能相同 print $str = "a1234" =~ m:^[a-zA-Z0-9]{4,16}$: ? "COMFIRM" : "FAILED"; print $str = "a1234" =~ m"^\w[12]\d{8}$" ? "COMFIRM" : "INVAILD"; 如何写出高效率的正则表达式 如果纯粹是为了挑战自己的正则水平,用来实现一些特效(例如使用正则表达式计算质数、解线性方程),效率不是问题;如果所写的正则表达式只是为了满 足一两次、几十次的运行,优化与否区别也不太大。但是,如果所写的正则表达式会百万次、千万次地运行,效率就是很大的问题了。我这里总结了几条提升正则表 达式运行效率的经验(工作中学到的,看书学来的,自己的体会),贴在这里。如果您有其它的经验而这里没有提及,欢迎赐教。 为行文方便,先定义两个概念。 误匹配:指正则表达式所匹配的内容范围超出了所需要范围,有些文本明明不符合要求,但是被所写的正则式“击中了”。例如,如果使用\d{11}来匹配11位的手机号,\d{11}不单能匹配正确的手机号,它还会匹配98765432100这样的明显不是手机号的字符串。我们把这样的匹配称之为误匹配。 漏匹配:指正则表达式所匹配的内容所规定的范围太狭窄,有些文本确实是所需要的,但是所写的正则没有将这种情况囊括在内。例如,使用\d{18}来匹配18位的身份证号码,就会漏掉结尾是字母X的情况。 写出一条正则表达式,既可能只出现误匹配(条件写得极宽松,其范围大于目标文本),也可能只出现漏匹配(只描述了目标文本中多种情况种的一种),还可能既有误匹配又有漏匹配。例如,使用\w+\.com来匹配.com结尾的域名,既会误匹配abc_.com这样的字串(合法的域名中不含下划线,\w包含了下划线这种情况),又会漏掉ab-c.com这样的域名(合法域名中可以含中划线,但是\w不匹配中划线)。 精准的正则表达式意味着既无误匹配且无漏匹配。当然,现实中存在这样的情况:只能看到有限数量的文本,根据这些文本写规则,但是这些规则将会用到海 量的文本中。这种情况下,尽可能地(如果不是完全地)消除误匹配以及漏匹配,并提升运行效率,就是我们的目标。本文所提出的经验,主要是针对这种情况。 掌握语法细节。正则表达式在各种语言中,其语法大致相同,细节各有千秋。明确所使用语言的正则的语法的细节,是写出正确、高效正则表达式的基础。例如,perl中与\w等效的匹配范围是[a-zA-Z0-9_];perl正则式不支持肯定逆序环视中使用可变的重复(variable repetition inside lookbehind,例如(?<=.*)abc),但是.Net语法是支持这一特性的;又如,JavaScript连逆序环视(Lookbehind,如(?<=ab)c) 都不支持,而perl和python是支持的。《精通正则表达式》第3章《正则表达式的特性和流派概览》明确地列出了各大派系正则的异同,这篇文章也简要 地列出了几种常用语言、工具中正则的比较。对于具体使用者而言,至少应该详细了解正在使用的那种工作语言里正则的语法细节。 先粗后精,先加后减。使用正则表达式语法对于目标文本进行描述和界定,可以像画素描一样,先大致勾勒出框架,再逐步在局步实现细节。仍举刚才的手机号的例子,先界定\d{11},总不会错;再细化为1[358]\d{9}, 就向前迈了一大步(至于第二位是不是3、5、8,这里无意深究,只举这样一个例子,说明逐步细化的过程)。这样做的目的是先消除漏匹配(刚开始先尽可能多 地匹配,做加法),然后再一点一点地消除误匹配(做减法)。这样有先有后,在考虑时才不易出错,从而向“不误不漏”这个目标迈进。 留有余地。所能看到的文本sample是有限的,而待匹配检验的文本是海量的,暂时不可见的。对于这样的情况,在写正则表达 式时要跳出所能见到的文本的圈子,开拓思路,作出“战略性前瞻”。例如,经常收到这样的垃圾短信:“发*票”、“发#漂”。如果要写规则屏蔽这样烦人的垃 圾短信,不但要能写出可以匹配当前文本的正则表达式 发[*#](?:票|漂),还要能够想到 发.(?:票|漂|飘)之类可能出现的“变种”。这在具体的领域或许会有针对性的规则,不多言。这样做的目的是消除漏匹配,延长正则表达式的生命周期。 明确。具体说来,就是谨慎用点号这样的元字符,尽可能不用星号和加号这样的任意量词。只要能确 定范围的,例如\w,就不要用点号;只要能够预测重复次数的,就不要用任意量词。例如,写析取twitter消息的脚本,假设一条消息的xml正文部分结 构是<span class=”msg”>…</span>且正文中无尖括号,那么<span class=”msg”>[^<]{1,480}</span>这种写法的思路要好于<span class=”msg”>.*</span>,原因有二:一是使用[^<],它保证了文本的范围不会超出下一个小于号所在的位置;二是明确长度范围,{1,480},其依据是一条twitter消息大致能的字符长度范围。当然,480这个长度是否正确还可推敲,但是这种思路是值得借鉴的。说得狠一点,“滥用点号、星号和加号是不环保、不负责任的做法”。 不要让稻草压死骆驼。每使用一个普通括号()而不是非捕获型括号(?:…),就会保留一部分内存等着你再次访问。这样的正则表达式、无限次地运行次数,无异于一根根稻草的堆加,终于能将骆驼压死。养成合理使用(?:…)括号的习惯。 宁简勿繁。将一条复杂的正则表达式拆分为两条或多条简单的正则表达式,编程难度会降低,运行效率会提升。例如用来消除行首和行尾空白字符的正则表达式s/^\s+|\s+$//g;,其运行效率理论上要低于s/^\s+//g; s/\s+$//g; 。这个例子出自《精通正则表达式》第五章,书中对它的评论是“它几乎总是最快的,而且显然最容易理解”。既快又容易理解,何乐而不为?工作中我们还有其它的理由要将C==(A|B)这 样的正则表达式拆为A和B两条表达式分别执行。例如,虽然A和B这两种情况只要有一种能够击中所需要的文本模式就会成功匹配,但是如果只要有一条子表达式 (例如A)会产生误匹配,那么不论其它的子表达式(例如B)效率如何之高,范围如何精准,C的总体精准度也会因A而受到影响。 巧妙定位。有时候,我们需要匹配的the,是作为单词的the(两边有空格),而不是作为单词一部分的t-h-e的有序排列(例如together中的the)。在适当的时候用上^,$,\b等等定位锚点,能有效提升找到成功匹配、淘汰不成功匹配的效率。 正则表达式: http://114.xixik.com/regex/
JDBC MySql常见的函数 MySql函数的介绍 使用MySql函数的目的 为了简化操作,MySql提供了大量的函数给程序员使用(比如你想输入当前时间,可以调用now()函数)
函数可以出现的位置 插入语句的values()中,更新语句中,删除语句中,查询语句及其子句中。
环境准备 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 CREATE TABLE t_user ( id int primary key AUTO_INCREMENT, uname varchar (40 ) DEFAULT NULL , age int DEFAULT NULL , sex int DEFAULT NULL ); insert into t_user values (null ,'zs' ,18 ,1 );insert into t_user values (null ,'ls' ,20 ,0 );insert into t_user values (null ,'ww' ,23 ,1 );insert into t_user values (null ,'zl' ,24 ,1 );insert into t_user values (null ,'lq' ,15 ,0 );insert into t_user values (null ,'hh' ,12 ,0 );insert into t_user values (null ,'wzx' ,60 ,null );insert into t_user values (null ,'lb' ,null ,null );
if相关函数 if函数 语法
说明: 如果 expr1 是TRUE,则 IF()的返回值为expr2 ; 否则返回值则为 expr3 。if() 的返回值为数字值或字符串值,具体情况视其所在语境而定。
示例 练习1:获取用户的姓名、性别,如果性别为1则显示1,否则显示0;要求使用if函数查询:
1 SELECT uname, IF(sex, 1 , 0 ) FROM t_user;
ifnull函数 语法
说明:假如expr1 不为 NULL,则 IFNULL() 的返回值为 expr1 ; 否则其返回值为 expr2 。ifnull()的返回值是数字或是字符串,具体情况取决于其所使用的语境。
示例 练习1:获取用户的姓名、性别,如果性别为null则显示为1;要求使用ifnull函数查询:
1 SELECT uname, IFNULL(sex, 1 ) FROM t_user;
字符串函数 字符串连接函数 字符串连接函数主要有2个:
函数或操作符
描述
concat(str1, str2, …)
字符串连接函数,可以将多个字符串进行连接
concat_ws(separator, str1, str2, …)
可以指定间隔符将多个字符串进行连接;
练习1:使用concat函数显示出 你好,uname 的结果
1 SELECT CONCAT('你好,' , uname) FROM t_user;
练习2:使用concat_ws函数显示出 你好,uname 的结果
1 SELECT CONCAT_WS(',' , '你好' , uname) FROM t_user;
字符串大小写处理函数 字符串大小写处理函数主要有2个:
函数或操作符
描述
upper(str)
得到str的大写形式
lower(str)
得到str的小写形式
练习1: 将字符串 hello 转换为大写显示
练习2:将字符串 heLLo 转换为小写显示
移除空格函数 可以对字符串进行按长度填充满、也可以移除空格符
函数或操作符
描述
trim(str)
将str两边的空白符移除
练习1: 将用户id位8的用户的姓名的两边空白符移除
1 2 SELECT TRIM (uname) FROM t_user WHERE id = 8 ;
子串函数 字符串也可以按条件进行截取,主要有以下可以截取子串的函数;
函数或操作符
描述
substr()、substring()
获取子串: 1:substr(str, pos) 、substring(str, pos); 2:substr(str, pos, len)、substring(str, pos, len)
练习1:获取 hello,world 从第二个字符开始的完整子串
1 SELECT SUBSTR("hello,world", 2 );
练习2:获取 hello,world 从第二个字符开始但是长度为4的子串
1 SELECT SUBSTR("hello,world", 2 , 4 );
时间日期函数 mysql提供了一些用于获取特定时间的函数:
函数或操作符
描述
current_date()
获取当前日期,如 2019-10-18
current_tme()
获取当前时:分:秒,如:15:36:11
now()
获取当前的日期和时间,如:2019-10-18 15:37:17
练习1:获取当前的日期
练习2: 获取当前的时间(仅仅需要时分秒)
练习3: 获取当前时间(包含年月日时分秒)
数值函数 常见的数值相关函数如下表:
函数或操作符
描述
abs(x)
获取数值x的绝对值
ceil(x)
向上取整,获取不小于x的整数值
floor(x)
向下取整,获取不大于x的整数值
pow(x, y)
获取x的y次幂
rand()
获取一个0-1之间的随机浮点数
练习1: 获取 -12 的绝对值
练习2: 将 -11.2 向上取整
练习3: 将 1.6 向下取整
练习4: 获得2的32次幂的值
练习5: 获得一个在0-100之间的随机数
JDBC入门 JDBC概述
没有JDBC
有了JDBC后
什么是JDBC JDBC(java database connectivity): sun公司为了简化和统一java连接数据库,定义的一套规范(API,接口).
JDBC和驱动的关系 接口(JDBC)与实现(驱动jar包)的关系
小结
为什么要学习JDBC? 为了java连接/操作数据库更加的简单方便, 学习成本减低
JDBC和驱动关系?
JDBC快速入门 准备工作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 create database day18;use day18; create table user ( id int primary key auto_increment, username varchar (20 ), password varchar (20 ), nickname varchar (20 ) ); INSERT INTO `USER ` VALUES (null ,'zs' ,'123456' ,'老张' );INSERT INTO `USER ` VALUES (null ,'ls' ,'123456' ,'老李' );INSERT INTO `USER ` VALUES (null ,'wangwu' ,'123' ,'东方不败' );
需求 查询所有的用户, 输出到控制台
步骤
创建Java工程, 拷贝驱动jar包
加载驱动
获得连接
创建执行sql语句对象
执行sql语句, 处理结果
释放资源
代码实现
驱动jar包导入项目
代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 public class Test { public static void main (String[] args) throws Exception{ DriverManager.registerDriver(new Driver ()); String url = "jdbc:mysql://localhost:3306/day20_1" ; String username = "root" ; String password = "root" ; Connection connection = DriverManager.getConnection(url,username,password); Statement statement = connection.createStatement(); String sql = "select * from user" ; ResultSet resultSet = statement.executeQuery(sql); while (resultSet.next()) { System.out.print(resultSet.getObject(1 )+" " ); System.out.print(resultSet.getObject(2 )+" " ); System.out.print(resultSet.getObject(3 )+" " ); System.out.println(resultSet.getObject(4 )); System.out.println("------------------------------------------------" ); } if (resultSet != null ){ resultSet.close(); } if (statement != null ){ statement.close(); } if (connection != null ){ connection.close(); } } }
小结
加载,注册驱动
获得连接
创建执行sql语句对象
执行sql语句,处理结果
释放资源
JDBC API详解
API-Drivermanager类 能够使用DriverManager类
1.registerDriver(Driver driver) ;注册驱动
1 2 3 4 5 6 7 static { try { java.sql.DriverManager.registerDriver(new Driver()); } catch (SQLException E) { throw new RuntimeException("Can't register driver!"); } }
翻阅源码发现,通过API的方式注册驱动,Driver会new两次,所有推荐这种写法:
Class.forName("com.mysql.jdbc.Driver"); //当前就理解成 可以让com.mysql.jdbc.Driver里面的静态代码块执行
getConnection(String url, String user, String password) ;与数据库建立连接
作用:
注册驱动
获得连接
API-Connection接口 能够使用Connection接口
概述: 接口的实现在数据库驱动中。所有与数据库交互都是基于连接对象 的。
作用
1 2 createStatement() ;创建执行sql语句对象 prepareStatement(String sql) ;创建预编译执行sql语句的对象
Connection代表连接对象, 是一个接口, 实现在驱动jar包; 操作数据库都是基于Connection的
作用: connection.createStatement();
创建执行sql语句对象
API-Statement接口 能够使用Statement接口
概述: 接口的实现在数据库驱动中. 用来**操作sql语句(增删改查)**,并返回相应结果对象
作用
1 2 3 4 5 ResultSet executeQuery (String sql) 根据查询语句返回结果集。只能执行**select**语句。 int executeUpdate (String sql) 根据执行的DML(insert update delete)语句,返回受影响的行数。boolean execute (String sql) 此方法可以执行任意sql语句。返回boolean 值. 【了解】 true : 执行select有查询的结果 false : 执行insert, delete,update, 执行select没有查询的结果
statement 作用用来执行sql语句
执行查询
1 ResultSet excuteQuery(String sql); //返回值是结果集
执行增删改
1 int excuteUpdate(String sql); //返回值是受影响的行数
API-ResultSet接口 掌握ResultSet接口的使用
封装结果集,查询结果表的对象;
提供一个游标,默认游标指向结果集第一行之前。
调用一次next(),游标向下移动一行。
提供一些get方法。
ResultSet接口常用API
boolean next();将光标从当前位置向下移动一行
int getInt(int colIndex)以int形式获取ResultSet结果集当前行指定列号值
int getInt(String colLabel)以int形式获取ResultSet结果集当前行指定列名值
float getFloat(int colIndex)以float形式获取ResultSet结果集当前行指定列号值
float getFloat(String colLabel)以float形式获取ResultSet结果集当前行指定列名值
String getString(int colIndex)以String 形式获取ResultSet结果集当前行指定列号值
String getString(String colLabel)以String形式获取ResultSet结果集当前行指定列名值
Date getDate(int columnIndex); 以Date 形式获取ResultSet结果集当前行指定列号值
Date getDate(String columnName);以Date形式获取ResultSet结果集当前行指定列名值
void close()关闭ResultSet 对象
图解
封装
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 package com.nbchen.bean;public class User { private int id; private String username; private String password; private String nickname; public User () { } public User (int id, String username, String password, String nickname) { this .id = id; this .username = username; this .password = password; this .nickname = nickname; } public int getId () { return id; } public void setId (int id) { this .id = id; } 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; } public String getNickname () { return nickname; } public void setNickname (String nickname) { this .nickname = nickname; } @Override public String toString () { return "User{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", nickname='" + nickname + '\'' + '}' ; } }
1 2 3 4 5 6 7 8 9 10 List<User> list = new ArrayList <User>(); while (resultSet.next()) { User user = new User (resultSet.getInt("id" ), resultSet.getString("username" ), resultSet.getString("password" ), resultSet.getString("nickname" )); list.add(user); }
API-总结 API
DriverManager:驱动管理器
Connection: 代表连接对象
Statement: 执行sql语句对象
执行查询 Result executeQuery(String sql) 返回结果集
执行增删改 int excuteUpdate(String sql) 返回受影响的行数
ResultSet: 结果集
boolen next() 每调用一次, 光标就向下移动一行; 这个行有数据, 返回true; 没有数据, 返回false
get类型(String 列名); 根据列名 获得当前列的数据
注意事项
JDBC操作数据库练习 单元测试介绍和使用 JUnit介绍 JUnit是一个Java语言的单元测试jar。属于第三方工具,一般情况下需要导入jar包,不过,多数Java开发环境已经集成了JUnit作为单元测试工具.编写测试类,简单理解可以用于取代java的main方法
使用
添加IDEA中集成的Junit库,使用快捷键“Alt+Enter”,点击“Add Junit …”
使用:选中方法右键,执行当前方法或者选中类名右键,执行类中所有方法(方法必须标记@Test)
小结
常见使用错误,如果没有添加“@Test”,使用“Junit Test”进行运行,将抛异常
单元测试需要注意的地方:
增删改查练习
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 package com.nbchen.demo6_JDBC增删改查练习;import com.nbchen.demo1_JDBC快速入门.User;import org.junit.Test;import java.net.URL;import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultSet;import java.sql.Statement;import java.util.ArrayList;public class Demo { @Test public void insert () throws Exception{ Class.forName("com.mysql.jdbc.Driver" ); String url = "jdbc:mysql://localhost:3306/day20_1" ; String user = "root" ; String password = "root" ; Connection connection = DriverManager.getConnection(url, user, password); Statement statement = connection.createStatement(); String sql = "insert into user values(null,'zl','123456','赵六')" ; int rows = statement.executeUpdate(sql); System.out.println("受影响的行数:" +rows); if (statement != null ){ statement.close(); } if (connection != null ){ connection.close(); } } @Test public void update () throws Exception{ Class.forName("com.mysql.jdbc.Driver" ); String url = "jdbc:mysql://localhost:3306/day20_1" ; String user = "root" ; String password = "root" ; Connection connection = DriverManager.getConnection(url, user, password); Statement statement = connection.createStatement(); String sql = "update user set password = '123456' where username = 'wangwu'" ; int rows = statement.executeUpdate(sql); System.out.println("受影响的行数:" +rows); if (statement != null ){ statement.close(); } if (connection != null ){ connection.close(); } } @Test public void delete () throws Exception{ Class.forName("com.mysql.jdbc.Driver" ); String url = "jdbc:mysql://localhost:3306/day20_1" ; String user = "root" ; String password = "root" ; Connection connection = DriverManager.getConnection(url, user, password); Statement statement = connection.createStatement(); String sql = "delete from user where id = 3" ; int rows = statement.executeUpdate(sql); System.out.println("受影响的行数:" +rows); if (statement != null ){ statement.close(); } if (connection != null ){ connection.close(); } } @Test public void select1 () throws Exception{ Class.forName("com.mysql.jdbc.Driver" ); String url = "jdbc:mysql://localhost:3306/day20_1" ; String user = "root" ; String password = "root" ; Connection connection = DriverManager.getConnection(url, user, password); Statement statement = connection.createStatement(); String sql = "select * from user" ; ResultSet resultSet = statement.executeQuery(sql); ArrayList<User> list = new ArrayList <>(); while (resultSet.next()) { User use = new User (); use.setId(resultSet.getInt("id" )); use.setUsername(resultSet.getString("username" )); use.setPassword(resultSet.getString("password" )); use.setNickname(resultSet.getString("nickname" )); list.add(use); } if (resultSet != null ){ resultSet.close(); } if (statement != null ){ statement.close(); } if (connection != null ){ connection.close(); } for (User user1 : list) { System.out.println(user1); } } @Test public void select2 () throws Exception{ Class.forName("com.mysql.jdbc.Driver" ); String url = "jdbc:mysql://localhost:3306/day20_1" ; String user = "root" ; String password = "root" ; Connection connection = DriverManager.getConnection(url, user, password); Statement statement = connection.createStatement(); String sql = "select * from user where username = 'zs' and password = '123456'" ; ResultSet resultSet = statement.executeQuery(sql); User use = null ; while (resultSet.next()) { use = new User (); use.setId(resultSet.getInt("id" )); use.setUsername(resultSet.getString("username" )); use.setPassword(resultSet.getString("password" )); use.setNickname(resultSet.getString("nickname" )); } if (resultSet != null ){ resultSet.close(); } if (statement != null ){ statement.close(); } if (connection != null ){ connection.close(); } if (use == null ){ System.out.println("失败" ); }else { System.out.println("成功" ); } } }
JDBC工具类的抽取
1 2 3 4 driver =com.mysql.jdbc.Driver url =jdbc:mysql://localhost:3306/day20_1 username =root password =root
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 package com.nbchen.Utils;import java.io.FileInputStream;import java.io.InputStream;import java.sql.*;import java.util.Properties;public class JDBCUtils { private static String driver; private static String url; private static String user; private static String password; static { try { Properties pro = new Properties (); InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream("db.properties" ); pro.load(is); url = pro.getProperty("url" ); user = pro.getProperty("user" ); password = pro.getProperty("password" ); driver = pro.getProperty("driver" ); Class.forName(driver); } catch (Exception e) { e.printStackTrace(); } } public static Connection getConnection () throws Exception { Connection connection = DriverManager.getConnection(url, user, password); return connection; } public static void release (ResultSet resultSet, Statement statement, Connection connection) { if (resultSet != null ) { try { resultSet.close(); } catch (SQLException e) { e.printStackTrace(); } } if (statement != null ) { try { statement.close(); } catch (SQLException e) { e.printStackTrace(); } } if (connection != null ) { try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
小结
注意事项
PreparedStatement 登录案例 需求 在控制台输入用户名和密码,查询数据库,如果数据库存在当前用户,显示登录成功!
如果数据库不存在当前用户,显示登录失败!
分析 登录是做什么
登录说白了就是根据==用户名和密码查询数据库==, 如果能查询出来就是登录成功, 查询不出来就是登录失败
思路分析
代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 package com.nbchen.demo5_登录案例;import com.nbchen.demo2_JDBC_API详解.User;import com.nbchen.demo4_JDBC工具类的抽取.JDBCUtils;import java.sql.Connection;import java.sql.ResultSet;import java.sql.Statement;import java.util.Scanner;public class Test { public static void main (String[] args) throws Exception{ Scanner sc = new Scanner (System.in); System.out.println("请输入用户名:" ); String username = sc.nextLine(); System.out.println("请输入密码:" ); String password = sc.nextLine(); Connection connection = JDBCUtils.getConnection(); Statement statement = connection.createStatement(); String sql = "select * from user where username = '" +username+"' and password = '" +password+"'" ; ResultSet resultSet = statement.executeQuery(sql); User user = null ; while (resultSet.next()) { user = new User (); user.setId(resultSet.getInt("id" )); user.setUsername(resultSet.getString("username" )); user.setPassword(resultSet.getString("password" )); user.setNickname(resultSet.getString("nickname" )); } JDBCUtils.release(resultSet,statement,connection); if (user == null ){ System.out.println("登录失败!" ); }else { System.out.println("登录成功,欢迎回来!" ); } } }
小结 登录思路
登录说白了就是根据用户名和密码查询数据库
登录思路
获得用户输入的用户名和密码
使用Jdbc根据用户名和密码查询数据库 封装成User对象
判断是否登录成功(判断User是否为null)
登录成功 打印 ‘登录成功’
登录失败 打印 登录失败’
SQL注入问题出现 当输入的密码 ' or '' = '
, 发现永远登录成功
SQL注入问题分析
1 2 3 4 SELECT * FROM user WHERE username = 'zs' AND password = '' or '' = '' SELECT * FROM user WHERE username = 'zs' AND password = '' or true SELECT * FROM user WHERE true SELECT * FROM user
登录中SQL注入问题解决 preparedStatement概述 预编译SQL语句对象, 是Statemen接口的子接口。
特点:
性能要比Statement高
会把sql语句先编译,格式固定好,
sql语句中的参数会发生变化,过滤掉用户输入的关键字(eg: or)
用法 通过connection对象创建
设置参数
prepareStatement.set类型(int i,Object obj);参数1 i 指的就是问号的索引(指第几个问号,从1开始),参数2就是值 eg: setString(1,”zs”); setString(2,”123456”);
执行
ResultSet executeQuery(); 执行查询语句
int executeUpdate();执行增删改语句
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 #### 实现 ```java package com.nbchen.demo6_登录中SQL注入问题解决; import com.nbchen.demo2_JDBC_API详解.User; import com.nbchen.demo4_JDBC工具类的抽取.JDBCUtils; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.Statement; import java.util.Scanner; public class Test { public static void main(String[] args) throws Exception{ /* 需求: 在控制台输入用户名和密码,查询数据库,如果数据库存在当前用户,显示登录成功! 如果数据库不存在当前用户,显示登录失败! */ // 0.获取控制台输入的用户名和密码 Scanner sc = new Scanner(System.in); System.out.println("请输入用户名:"); String username = sc.nextLine(); System.out.println("请输入密码:"); String password = sc.nextLine(); // 1.注册驱动,获得连接 Connection connection = JDBCUtils.getConnection(); // 2.创建预编译sql语句对象 String sql = "select * from user where username = ? and password = ?"; PreparedStatement ps = connection.prepareStatement(sql);// 固定sql语句的格式 // 3.设置参数值 ps.setString(1,username); ps.setString(2,password); // 3.执行sql语句,处理结果 ResultSet resultSet = ps.executeQuery();// 注意: 这里不需要传sql语句 // 创建User对象,封装数据 User user = null; while (resultSet.next()) { user = new User();// 查询到了就new对象 user.setId(resultSet.getInt("id")); user.setUsername(resultSet.getString("username")); user.setPassword(resultSet.getString("password")); user.setNickname(resultSet.getString("nickname")); } // 4.释放资源 JDBCUtils.release(resultSet,ps,connection); // 5.判断结果,显示信息(判断user对象是否为null,如果为null说明就没有查询到数据,否则就查询到了) if (user == null){ // 没有查询到 System.out.println("登录失败!"); }else{ // 查询到 System.out.println("登录成功,欢迎回来!"); } } }
使用preparedStatement完成CRUD 需求 通过PreparedStatement完成增、删、改、查
分析
注册驱动
获得连接
创建预编译sql语句对象
设置参数 执行
释放资源
实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 package com.nbchen.demo10_使用preparedStatement完成CRUD;import com.nbchen.Utils.JDBCUtils;import com.nbchen.demo1_JDBC快速入门.User;import org.junit.Test;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.util.ArrayList;public class Demo { @Test public void insert () throws Exception{ Connection connection = JDBCUtils.getConnection(); String sql = "insert into user values(null,?,?,?)" ; PreparedStatement ps = connection.prepareStatement(sql); ps.setString(1 ,"tq" ); ps.setString(2 ,"123456" ); ps.setString(3 ,"田七" ); int rows = ps.executeUpdate(); System.out.println("受影响的行数:" +rows); JDBCUtils.release(null , ps, connection); } @Test public void update () throws Exception{ Connection connection = JDBCUtils.getConnection(); String sql = "update user set password = ? where id = ?" ; PreparedStatement ps = connection.prepareStatement(sql); ps.setString(1 ,"abcdef" ); ps.setInt(2 ,7 ); int rows = ps.executeUpdate(); System.out.println("受影响的行数:" +rows); JDBCUtils.release(null , ps, connection); } @Test public void delete () throws Exception{ Connection connection = JDBCUtils.getConnection(); String sql = "delete from user where id = ?" ; PreparedStatement ps = connection.prepareStatement(sql); ps.setInt(1 ,5 ); int rows = ps.executeUpdate(); System.out.println("受影响的行数:" +rows); JDBCUtils.release(null , ps, connection); } @Test public void select1 () throws Exception{ Connection connection = JDBCUtils.getConnection(); String sql = "select * from user" ; PreparedStatement ps = connection.prepareStatement(sql); ResultSet resultSet = ps.executeQuery(); ArrayList<User> list = new ArrayList <User>(); while (resultSet.next()) { User user = new User (); user.setId(resultSet.getInt("id" )); user.setUsername(resultSet.getString("username" )); user.setPassword(resultSet.getString("password" )); user.setNickname(resultSet.getString("nickname" )); list.add(user); } JDBCUtils.release(resultSet, ps, connection); for (User user : list) { System.out.println(user); } } @Test public void select2 () throws Exception{ Connection connection = JDBCUtils.getConnection(); String sql = "select * from user where username = ?" ; PreparedStatement ps = connection.prepareStatement(sql); ps.setString(1 ,"zs" ); ResultSet resultSet = ps.executeQuery(); User user = null ; while (resultSet.next()) { user = new User (); user.setId(resultSet.getInt("id" )); user.setUsername(resultSet.getString("username" )); user.setPassword(resultSet.getString("password" )); user.setNickname(resultSet.getString("nickname" )); } JDBCUtils.release(resultSet, ps, connection); System.out.println(user); } }
小结 步骤
注册驱动
获得连接
创建预编译sql语句对象
设置参数,
执行sql语句
释放资源
API
创建预编译sql语句对象
1 connection.prepareStatement(String sql); //sql里面有参数, 先用?代替,进行占位
设置参数
1 prepareStatement.set类型(int 第几个问号,Object 值);
执行
1 2 Result result = prepareStatement.excuteQuery(); //执行查询 不传sql语句 int rows = prepareStatement.excuteUpdate(); //执行增删改 不传sql语句
注意事项
?只能占参数,说白了就是列的值
?从1开始计 数
执行的时候不要传入sql语句
JDBC事务的处理 JDBC事务介绍 之前我们是使用MySQL的命令来操作事务。接下来我们使用JDBC来操作事务. 先来学习下相关的API
Connection中与事务有关的方法
说明
setAutoCommit(boolean autoCommit)
参数是true或false 如果设置为false,表示关闭自动提交,相当于开启事务; 类似sql里面的 start transaction;
void commit()
提交事务; 类似sql里面的 commit;
void rollback()
回滚事务; 类似sql里面的 rollback;
代码演示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 public class Test { public static void main (String[] args) throws Exception{ Connection connection = JDBCUtils.getConnection(); connection.setAutoCommit(false ); String sql = "update user set password = ? where id = ?" ; PreparedStatement ps = connection.prepareStatement(sql); ps.setString(1 , "666666" ); ps.setInt(2 ,6 ); int rows = ps.executeUpdate(); System.out.println("受影响的行数:" +rows); connection.rollback(); JDBCUtils.release(null ,ps,connection); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 public class Test { public static void main (String[] args) { Connection connection = null ; PreparedStatement ps = null ; try { connection = JDBCUtils.getConnection(); connection.setAutoCommit(false ); String sql = "update user set password = ? where id = ?" ; ps = connection.prepareStatement(sql); ps.setString(1 , "666666" ); ps.setInt(2 , 6 ); int rows = ps.executeUpdate(); System.out.println("受影响的行数:" + rows); System.out.println(1 /0 ); connection.commit(); } catch (Exception e) { e.printStackTrace(); try { connection.rollback(); } catch (SQLException e1) { e1.printStackTrace(); } } finally { JDBCUtils.release(null , ps, connection); } } }
转账案例
1 2 3 4 5 6 7 8 9 create table account( id int primary key auto_increment, name varchar (20 ), money double ); insert into account values (null ,'zs' ,1000 );insert into account values (null ,'ls' ,1000 );insert into account values (null ,'ww' ,1000 );
需求 zs给ls转100, 使用事务进行控制
分析
实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 public class Test { public static void main (String[] args) { Connection connection = null ; PreparedStatement ps1 = null ; PreparedStatement ps2 = null ; try { connection = JDBCUtils.getConnection(); connection.setAutoCommit(false ); String sql1 = "update account set money = money - ? where name = ?" ; String sql2 = "update account set money = money + ? where name = ?" ; ps1 = connection.prepareStatement(sql1); ps2 = connection.prepareStatement(sql2); ps1.setDouble(1 ,100 ); ps1.setString(2 ,"zs" ); ps1.executeUpdate(); System.out.println(1 /0 ); ps2.setDouble(1 ,100 ); ps2.setString(2 ,"ls" ); ps2.executeUpdate(); connection.commit(); } catch (Exception e) { e.printStackTrace(); try { connection.rollback(); } catch (SQLException e1) { e1.printStackTrace(); } } finally { JDBCUtils.release(null ,ps1,connection); JDBCUtils.release(null ,ps2,null ); } } }
小结
涉及到两个写的操作,我们一般通过手动事务去控制
JDBC操作事务API
1 2 3 connection.setAutoCommit(fasle); //开启事务 connection.commit(); //提交事务 connection.rollback(); //回滚事务
自定义连接池 连接池概念 为什么要使用连接池 Connection对象在JDBC使用的时候就会去创建一个对象,使用结束以后就会将这个对象给销毁了(close).每次创建和销毁对象都是耗时操作.需要使用连接池对其进行优化.
程序初始化的时候,初始化多个连接,将多个连接放入到池(集合)中.每次获取的时候,都可以直接从连接池中进行获取.使用结束以后,将连接归还到池中.
生活里面的连接池例子
老方式:
下了地铁需要骑车, 跑去生产一个, 然后骑完之后,直接把车销毁了.
连接池方式 摩拜单车:
骑之前, 有一个公司生产了很多的自行车, 下了地铁需要骑车, 直接扫码使用就好了, 然后骑完之后, 还回去
连接池原理【重点】
程序一开始就创建一定数量的连接,放在一个容器(集合)中,这个容器称为连接池。
使用的时候直接从连接池中取一个已经创建好的连接对象, 使用完成之后 归还到池子
如果池子里面的连接使用完了, 还有程序需要使用连接, 先等待一段时间(eg: 3s), 如果在这段时间之内有连接归还, 就拿去使用; 如果还没有连接归还, 新创建一个, 但是新创建的这一个不会归还了(销毁)
集合选择LinkedList
增删比较快
LinkedList里面的removeFirst()和addLast()方法和连接池的原理吻合
小结
使用连接池的目的: 可以让连接得到复用, 避免浪费
自定义连接池-初级版本 目标 根据连接池的原理, 使用LinkedList自定义连接池
分析
创建一个类MyDataSource, 定义一个集合LinkedList
程序初始化的时候, 创建5个连接 存到LinkedList
定义getConnection() 从LinkedList取出Connection返回
定义addBack()方法归还Connection到LinkedList
实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 public class MyDataSource1 { private static LinkedList<Connection> list = new LinkedList <>(); static { for (int i = 0 ; i < 5 ; i++) { try { Connection connection = JDBCUtils.getConnection(); list.add(connection); } catch (Exception e) { e.printStackTrace(); } } } public Connection getAbc () { Connection connection = list.removeFirst(); return connection; } public void addBack (Connection connection) { list.addLast(connection); } public static int getCount () { return list.size(); } } public class CRUDTest1 { @Test public void select () throws Exception { MyDataSource1 dataSource = new MyDataSource1 (); System.out.println("获得连接之前,连接池中连接的数量:" +MyDataSource1.getCount()); Connection connection = dataSource.getAbc(); String sql = "select * from user where id = ?" ; PreparedStatement ps = connection.prepareStatement(sql); ps.setInt(1 ,3 ); ResultSet resultSet = ps.executeQuery(); User user = null ; while (resultSet.next()){ user = new User (); user.setId(resultSet.getInt("id" )); user.setUsername(resultSet.getString("username" )); user.setPassword(resultSet.getString("password" )); user.setNickname(resultSet.getString("nickname" )); } System.out.println("获得连接之后,连接池中连接的数量:" +MyDataSource1.getCount()); System.out.println(user); dataSource.addBack(connection); JDBCUtils.release(resultSet,ps,null ); System.out.println("归还连接之后,连接池中连接的数量:" +MyDataSource1.getCount()); } }
小结
创建一个类MyDataSource, 定义一个集合LinkedList
程序初始化(静态代码块)里面 创建5个连接存到LinkedList
定义提供Connection的方法
定义归还Connection的方法
自定义连接池-进阶版本 目标 实现datasource完成自定义连接池
分析 在初级版本版本中, 我们定义的方法是getAbc(). 因为是自定义的.如果改用李四的自定义的连接池,李四定义的方法是getCon(), 那么我们的源码就需要修改, 这样不方便维护. 所以sun公司定义了一个接口DataSource,让自定义连接池有了规范
讲解 datasource接口概述 Java为数据库连接池提供了公共的接口:javax.sql.DataSource,各个厂商(用户)需要让自己的连接池实现这个接口。这样应用程序可以方便的切换不同厂商的连接池!
代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 public class MyDataSource2 implements DataSource { private static LinkedList<Connection> list = new LinkedList <>(); static { for (int i = 0 ; i < 5 ; i++) { try { Connection connection = JDBCUtils.getConnection(); list.add(connection); } catch (Exception e) { e.printStackTrace(); } } } public void addBack (Connection connection) { list.addLast(connection); } public static int getCount () { return list.size(); } @Override public Connection getConnection () throws SQLException { Connection connection = list.removeFirst(); return connection; } @Override public Connection getConnection (String username, String password) throws SQLException { return null ; } @Override public PrintWriter getLogWriter () throws SQLException { return null ; } @Override public void setLogWriter (PrintWriter out) throws SQLException { } @Override public void setLoginTimeout (int seconds) throws SQLException { } @Override public int getLoginTimeout () throws SQLException { return 0 ; } @Override public Logger getParentLogger () throws SQLFeatureNotSupportedException { return null ; } @Override public <T> T unwrap (Class<T> iface) throws SQLException { return null ; } @Override public boolean isWrapperFor (Class<?> iface) throws SQLException { return false ; } } public class CRUDTest2 { @Test public void select () throws Exception { DataSource dataSource = new MyDataSource2 (); System.out.println("获得连接之前,连接池中连接的数量:" +MyDataSource2.getCount()); Connection connection = dataSource.getConnection(); String sql = "select * from user where id = ?" ; PreparedStatement ps = connection.prepareStatement(sql); ps.setInt(1 ,3 ); ResultSet resultSet = ps.executeQuery(); User user = null ; while (resultSet.next()){ user = new User (); user.setId(resultSet.getInt("id" )); user.setUsername(resultSet.getString("username" )); user.setPassword(resultSet.getString("password" )); user.setNickname(resultSet.getString("nickname" )); } System.out.println("获得连接之后,连接池中连接的数量:" +MyDataSource2.getCount()); System.out.println(user); ((MyDataSource2)dataSource).addBack(connection); JDBCUtils.release(resultSet,ps,null ); System.out.println("归还连接之后,连接池中连接的数量:" +MyDataSource2.getCount()); } }
小结 编写连接池遇到的问题
实现DataSource接口后,addBack()不能调用了.
能不能不引入新的api,直接调用之前的connection.close(),但是这个close不是关闭,是归还
解决办法
继承
装饰者模式
作用:改写已存在的类的某个方法或某些方法
条件:
增强类和被增强类实现的是同一个接口
增强类里面要拿到被增强类的引用
动态代理
自定义连接池-终极版本 使用装饰者模式改写connection的close()方法, 让connection归还
装饰者模式
概述
什么是装饰者模式
装饰者模式,是 23种常用的面向对象软件的设计模式之一. 动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更加有弹性的替代方案。
装饰者的作用:改写已存在的类的某个方法或某些方法, 增强方法的逻辑
使用装饰者模式需要满足的条件
增强类和被增强类实现的是同一个接口
增强类里面要拿到被增强类的引用
装饰者模式的使用【重点】 实现步骤:
增强类和被增强类需要实现同一个接口
增强类里面需要得到被增强类的引用,
对于不需要改写的方法,调用被增强类原有的方法。
对于需要改写的方法写自己的代码
1 2 3 4 public interface Star { public void sing () ; public void dance () ; }
1 2 3 4 5 6 7 8 9 10 11 12 public class LiuDeHua implements Star { @Override public void sing () { System.out.println("刘德华唱忘情水..." ); } @Override public void dance () { System.out.println("刘德华跳霹雳舞..." ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class LiuDeHuaWrapper implements Star { LiuDeHua ldh; public LiuDeHuaWrapper (LiuDeHua ldh) { this .ldh = ldh; } @Override public void sing () { System.out.println("刘德华唱冰雨..." ); ldh.sing(); System.out.println("刘德华唱练习..." ); } @Override public void dance () { ldh.dance(); } }
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 public class Test { public static void main (String[] args) { LiuDeHua ldh = new LiuDeHua (); LiuDeHuaWrapper ldhw = new LiuDeHuaWrapper (ldh); ldhw.sing(); ldhw.dance(); } }
自定义连接池终极版本 分析 增强connection的close()方法, 其它的方法逻辑不改
创建MyConnection实现Connection
在MyConnection里面需要得到被增强的connection对象(通过构造方法传进去)
改写close()的逻辑, 变成归还
其它方法的逻辑, 还是调用被增强connection对象之前的逻辑
实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public class MyConnection implements Connection { Connection con; LinkedList<Connection> list; public MyConnection (Connection con, LinkedList<Connection> list) { this .con = con; this .list = list; } @Override public void close () throws SQLException { list.addLast(con); } @Override public Statement createStatement () throws SQLException { return con.createStatement(); } @Override public PreparedStatement prepareStatement (String sql) throws SQLException { return con.prepareStatement(sql); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 public class MyDataSource3 implements DataSource { private static LinkedList<Connection> list = new LinkedList <>(); static { for (int i = 0 ; i < 5 ; i++) { try { Connection connection = JDBCUtils.getConnection(); list.add(connection); } catch (Exception e) { e.printStackTrace(); } } } public static int getCount () { return list.size(); } @Override public Connection getConnection () throws SQLException { Connection connection = list.removeFirst(); MyConnection myConnection = new MyConnection (connection,list); return myConnection; } @Override public Connection getConnection (String username, String password) throws SQLException { return null ; } @Override public PrintWriter getLogWriter () throws SQLException { return null ; } @Override public void setLogWriter (PrintWriter out) throws SQLException { } @Override public void setLoginTimeout (int seconds) throws SQLException { } @Override public int getLoginTimeout () throws SQLException { return 0 ; } @Override public Logger getParentLogger () throws SQLFeatureNotSupportedException { return null ; } @Override public <T> T unwrap (Class<T> iface) throws SQLException { return null ; } @Override public boolean isWrapperFor (Class<?> iface) throws SQLException { return false ; } }
测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public class CRUDTest3 { @Test public void select () throws Exception { DataSource dataSource = new MyDataSource3 (); System.out.println("获得连接之前,连接池中连接的数量:" +MyDataSource3.getCount()); Connection connection = dataSource.getConnection(); String sql = "select * from user where id = ?" ; PreparedStatement ps = connection.prepareStatement(sql); ps.setInt(1 ,3 ); ResultSet resultSet = ps.executeQuery(); User user = null ; while (resultSet.next()){ user = new User (); user.setId(resultSet.getInt("id" )); user.setUsername(resultSet.getString("username" )); user.setPassword(resultSet.getString("password" )); user.setNickname(resultSet.getString("nickname" )); } System.out.println("获得连接之后,连接池中连接的数量:" +MyDataSource3.getCount()); System.out.println(user); JDBCUtils.release(resultSet,ps,connection); System.out.println("归还连接之后,连接池中连接的数量:" +MyDataSource3.getCount()); } }
小结
创建一个MyConnection实现Connection
在MyConnection得到被增强的connection对象
改写MyConnection里面的close()方法的逻辑为归还
MyConnection里面的其它方法 调用被增强的connection对象之前的逻辑
在MyDataSource03的getConnection()方法里面 返回了myConnection(增强的连接对象)
第三方连接池 常用连接池 通过前面的学习,我们已经能够使用所学的基础知识构建自定义的连接池了。其目的是锻炼大家的基本功,帮助大家更好的理解连接池的原理, 但现实是残酷的,我们所定义的 连接池 和第三方的连接池相比,还是显得渺小. 工作里面都会用第三方连接池.
常见的第三方连接池如下:
C3P0是一个开源的JDBC连接池 ,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。C3P0是异步操作的,所以一些操作时间过长的JDBC通过其它的辅助线程完成。目前使用它的开源项目有Hibernate,Spring等。C3P0有自动回收空闲连接功能
阿里巴巴-德鲁伊druid连接池 :Druid是阿里巴巴开源平台上的一个项目,整个项目由数据库连接池、插件框架和SQL解析器组成。该项目主要是为了扩展JDBC的一些限制,可以让程序员实现一些特殊的需求。
DBCP(DataBase Connection Pool)数据库连接池,是Apache上的一个Java连接池项目,也是Tomcat使用的连接池组件。dbcp没有自动回收空闲连接的功能。
C3P0 c3p0介绍
C3P0开源免费的连接池!目前使用它的开源项目有:Spring、Hibernate等。使用第三方工具需要导入jar包,c3p0使用时还需要添加配置文件c3p0-config.xml.
使用C3P0需要添加c3p0-0.9.1.2.jar
c3p0的使用 通过硬编码来编写【了解】 步骤
拷贝jar
创建C3P0连接池对象
从C3P0连接池对象里面获得connection
实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 public class Test1_ 通过硬编码来编写 { public static void main (String[] args) throws Exception { ComboPooledDataSource dataSource = new ComboPooledDataSource (); dataSource.setDriverClass("com.mysql.jdbc.Driver" ); dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/day21_1" ); dataSource.setUser("root" ); dataSource.setPassword("root" ); dataSource.setInitialPoolSize(5 ); Connection connection = dataSource.getConnection(); String sql = "select * from user where id = ?" ; PreparedStatement ps = connection.prepareStatement(sql); ps.setInt(1 , 3 ); ResultSet resultSet = ps.executeQuery(); User user = null ; while (resultSet.next()) { user = new User (); user.setId(resultSet.getInt("id" )); user.setUsername(resultSet.getString("username" )); user.setPassword(resultSet.getString("password" )); user.setNickname(resultSet.getString("nickname" )); } System.out.println(user); JDBCUtils.release(resultSet,ps,connection); } }
通过配置文件来编写【重点】 步骤:
拷贝jar
拷贝配置文件(c3p0-config.xml)到src目录【名字不要改 】
创建C3P0连接池对象【自动的读取】
从池子里面获得连接
实现:
编写配置文件c3p0-config.xml,放在src目录下(注:文件名一定不要改)
1 2 3 4 5 6 7 8 9 <c3p0-config > <default-config > <property name ="driverClass" > com.mysql.jdbc.Driver</property > <property name ="jdbcUrl" > jdbc:mysql://localhost:3306/day21_1</property > <property name ="user" > root</property > <property name ="password" > root</property > <property name ="initialPoolSize" > 5</property > </default-config > </c3p0-config >
编写Java代码 (会自动读取src目录下的c3p0-config.xml,所以不需要我们解析配置文件)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public class Test2_ 通过配置文件来编写 { public static void main (String[] args) throws Exception{ ComboPooledDataSource dataSource = new ComboPooledDataSource (); Connection connection = dataSource.getConnection(); String sql = "select * from user where id = ?" ; PreparedStatement ps = connection.prepareStatement(sql); ps.setInt(1 , 3 ); ResultSet resultSet = ps.executeQuery(); User user = null ; while (resultSet.next()) { user = new User (); user.setId(resultSet.getInt("id" )); user.setUsername(resultSet.getString("username" )); user.setPassword(resultSet.getString("password" )); user.setNickname(resultSet.getString("nickname" )); } System.out.println(user); JDBCUtils.release(resultSet,ps,connection); } }
使用c3p0改写工具类【重点】 我们之前写的工具类(JdbcUtils)每次都会创建一个新的连接, 使用完成之后, 都给销毁了; 所以现在我们要使用c3p0来改写工具类. 也就意味着,我们从此告别了JdbcUtils. 后面会使用c3p0写的工具类
思路:
创建C3P0Utils这个类
定义DataSource, 保证DataSource全局只有一个
定义getConnection()方法从DataSource获得连接
定义closeAll()方法 释放资源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 package Utils;import com.mchange.v2.c3p0.ComboPooledDataSource;import javax.sql.DataSource;import java.sql.Connection;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;public class C3P0Utils { private static final ComboPooledDataSource DATA_SOURCE = new ComboPooledDataSource (); public static Connection getConnection () throws Exception{ Connection connection = DATA_SOURCE.getConnection(); return connection; } public static DataSource getDataSource () { return DATA_SOURCE; } public static void release (ResultSet resultSet, Statement statement, Connection connection) { if (resultSet != null ) { try { resultSet.close(); } catch (SQLException e) { e.printStackTrace(); } } if (statement != null ) { try { statement.close(); } catch (SQLException e) { e.printStackTrace(); } } if (connection != null ) { try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
小结
C3P0 配置文件方式使用
拷贝jar
拷贝配置文件到src【配置文件的名字不要改】
创建C3P0连接池对象
C3P0工具类
保证DataSource连接池只有一个【static】
提供connection
释放资源
代替昨天写的JdbcUtils
DRUID DRUID介绍 Druid是阿里巴巴开发的号称为监控而生的数据库连接池,Druid是国内目前最好的数据库连接池。在功能、性能、扩展性方面,都超过其他数据库连接池。Druid已经在阿里巴巴部署了超过600个应用,经过一年多生产环境大规模部署的严苛考验。如:一年一度的双十一活动,每年春运的抢火车票。
Druid的下载地址:https://github.com/alibaba/druid
DRUID连接池使用的jar包:druid-1.0.9.jar
DRUID的使用 通过硬编码方式【了解】 步骤:
导入DRUID jar 包
创建Druid连接池对象, 配置4个基本参数
从Druid连接池对象获得Connection
实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 public class Test1_ 硬编码方式使用 { @Test public void select () throws Exception{ DruidDataSource dataSource = new DruidDataSource (); dataSource.setDriverClassName("com.mysql.jdbc.Driver" ); dataSource.setUrl("jdbc:mysql://localhost:3306/day21_1" ); dataSource.setUsername("root" ); dataSource.setPassword("root" ); dataSource.setInitialSize(5 ); DruidPooledConnection connection = dataSource.getConnection(); 78s String sql = "select * from user where id = ?" ; PreparedStatement ps = connection.prepareStatement(sql); ps.setInt(1 , 3 ); ResultSet resultSet = ps.executeQuery(); User user = null ; while (resultSet.next()) { user = new User (); user.setId(resultSet.getInt("id" )); user.setUsername(resultSet.getString("username" )); user.setPassword(resultSet.getString("password" )); user.setNickname(resultSet.getString("nickname" )); } System.out.println(user); JDBCUtils.release(resultSet,ps,connection); } }
通过配置文件方式【重点】 步骤:
导入DRUID jar 包
拷贝配置文件到src目录
根据配置文件创建Druid连接池对象
从Druid连接池对象获得Connection
实现:
创建druid.properties, 放在src目录下
1 2 3 4 url =jdbc:mysql://localhost:3306/day21_1 username =root password =root driverClassName =com.mysql.jdbc.Driver
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 public class Test2_ 配置文件方式使用 { @Test public void select () throws Exception{ Properties prop = new Properties (); InputStream is = Test2_通过配置文件来编写.class.getClassLoader().getResourceAsStream("druid.properties" ); prop.load(is); DataSource dataSource = DruidDataSourceFactory.createDataSource(prop); Connection connection = dataSource.getConnection(); String sql = "select * from user where id = ?" ; PreparedStatement ps = connection.prepareStatement(sql); ps.setInt(1 , 3 ); ResultSet resultSet = ps.executeQuery(); User user = null ; while (resultSet.next()) { user = new User (); user.setId(resultSet.getInt("id" )); user.setUsername(resultSet.getString("username" )); user.setPassword(resultSet.getString("password" )); user.setNickname(resultSet.getString("nickname" )); } System.out.println(user); JDBCUtils.release(resultSet,ps,connection); } }
问题 突然无法登录,数据库druid连接池满了,无法获取连接。
wait millis 60000,active 20,maxactive 20
DBUtils DBUtils的介绍 DBUtils的概述 DbUtils是Apache组织提供的一个对JDBC进行简单封装的开源工具类库,使用它能够简化JDBC应用程序的开发,同时也不会影响程序的性能
DBUtils的常用API介绍
创建QueryRunner对象的API
public QueryRunner(DataSource ds)
,提供数据源(连接池),DBUtils底层自动维护连接connection
QueryRunner执行增删改的SQL语句的API
int update(String sql, Object... params)
,执行增删改的SQL语句, params参数就是可变参数,参数个数取决于语句中问号的个数
1 eg: qr.update("insert into user values(null,?,?,?)","zs","123456","张三");
执行查询的SQL语句的API
query(String sql, ResultSetHandler<T> rsh, Object... params)
,其中ResultSetHandler是一个接口,表示结果集处理者
小结
DBUtils: Apache开发的一个数据库工具包, 用来简化JDBC操作数据库的步骤
JavaBean 目标
讲解
JavaBean说白了就是一个类, 用来封装数据用的
JavaBean要求
私有字段
提供公共的get/set方法
无参构造
建议满参构造
实现Serializable
字段和属性
字段: 全局/成员变量 eg: private String username
属性: 去掉get或者set首字母变小写 eg: setUsername-去掉set->Username-首字母变小写->username
一般情况下,我们通过IDEA直接生成的set/get 习惯把字段和属性搞成一样而言
小结
JavaBean用来封装数据用的
字段和属性
字段: 全局/成员变量 eg: private String username
属性: 去掉get或者set首字母变小写 eg: setUsername-去掉set->Username-首字母变小写->username
使用DBUtils完成增删改 目标
步骤
拷贝jar包
创建QueryRunner()对象,传入dataSource
调用update()方法
实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public class UpdateTest { @Test public void insert () throws Exception { QueryRunner qr = new QueryRunner (C3P0Utils.getDataSource()); String sql = "insert into user values(null,?,?,?)" ; int rows = qr.update(sql, "tianqi" , "123456" , "田七" ); System.out.println("受影响的行数:" + rows); } @Test public void delete () throws Exception { QueryRunner qr = new QueryRunner (C3P0Utils.getDataSource()); String sql = "delete from user where id = ?" ; int rows = qr.update(sql, 4 ); System.out.println("受影响的行数:" + rows); } @Test public void update () throws Exception{ QueryRunner qr = new QueryRunner (C3P0Utils.getDataSource()); String sql = "update user set password = ? where id = ?" ; int rows = qr.update(sql,"abcd" ,3 ); System.out.println("受影响的行数:" + rows); } }
小结
创建QueryRuner()对象, 传入DataSource
调用update(String sql, Object…params)
使用DBUtils完成查询 掌握使用DBUtils完成查询
步骤
拷贝jar包
创建QueryRunner()对象 传入DataSource
调用query(sql, resultSetHandler,params)方法
ResultSetHandler结果集处理类介绍
1 2 3 4 5 6 7 8 9 10 11 12 13 public queryRunner (DataSource ds) ;query(String sql, ResultSetHandler<T> rsh, Object... params) 执行查询语句 参数ResultSetHandler是一个接口,表示结果集处理者(对查询结果的封装): ResultSetHandler接口的实现类: ArrayHandler:适合查询结果是一条记录的,会把这条记录的数据封装到一个Object数组中 ArrayListHandler:适合查询结果是多条记录的,会把每条记录的数据封装到一个Object数组中,然后把这些数组添加到List集合中 BeanHandler:适合查询结果是一条记录的,会把这条记录的数据封装到一个javaBean对象中 BeanListHandler:适合查询结果是多条记录的,会把每条记录的数据封装到一个javaBean对象中,然后把这些javaBean对象添加到List集合中 ColumnListHandler:适合查询结果是单列多行的,会把该列的所有数据存储到List集合中 KeyedHandle:适合查询结果是多条记录的,会把每条记录的数据封装到一个Map集合中,然后把这些Map集合添加到另一个Map集合中 MapHandler:适合查询结果是一条记录的,会把这条记录的数据封装到一个Map集合中 MapListHandler:适合查询结果是多条记录的,会把每条记录的数据封装到一个Map集合中,然后把这些Map集合添加到List集合中 ScalarHandler:适合查询结果是单个值的,会把这个值封装成一个对象
查询一条数据封装到JavaBean对象中(使用BeanHandler) 1 2 3 4 5 6 7 8 9 10 11 @Test public void select1 () throws Exception{ QueryRunner qr = new QueryRunner (C3P0Utils.getDataSource()); String sql = "select * from user where id = ?" ; User user = qr.query(sql, new BeanHandler <User>(User.class), 3 ); System.out.println(user); }
查询多条数据封装到List<JavaBean>
中(使用BeanListHandler) 1 2 3 4 5 6 7 8 9 10 11 @Test public void select2 () throws Exception{ QueryRunner qr = new QueryRunner (C3P0Utils.getDataSource()); String sql = "select * from user" ; List<User> list = qr.query(sql, new BeanListHandler <User>(User.class)); for (User user : list) { System.out.println(user); } }
查询一条数据,封装到Map对象中(使用MapHandler) 1 2 3 4 5 6 7 8 9 10 11 12 13 @Test public void select3 () throws Exception { QueryRunner qr = new QueryRunner (C3P0Utils.getDataSource()); String sql = "select * from user where id = ?" ; Map<String, Object> map = qr.query(sql, new MapHandler (), 3 ); for (String key : map.keySet()) { System.out.println(key + ":" + map.get(key)); } }
查询多条数据,封装到List<Map>
对象中(使用MapListHandler) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Test public void select4 () throws Exception{ QueryRunner qr = new QueryRunner (C3P0Utils.getDataSource()); String sql = "select * from user" ; List<Map<String, Object>> list = qr.query(sql, new MapListHandler ()); for (Map<String, Object> map : list) { for (String key : map.keySet()) { System.out.println(key + ":" + map.get(key)); } System.out.println("=================================================" ); } }
查询单个数据(使用ScalarHandler()) 1 2 3 4 5 6 7 8 9 10 @Test public void select5 () throws Exception{ QueryRunner qr = new QueryRunner (C3P0Utils.getDataSource()); String sql = "select count(*) from user" ; Long count = (Long) qr.query(sql, new ScalarHandler ()); System.out.println("记录数:" +count); }
查询单列多个值(使用ArrayListHandler) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Test public void select6 () throws Exception{ QueryRunner qr = new QueryRunner (C3P0Utils.getDataSource()); String sql = "select username from user" ; List<Object[]> list = qr.query(sql, new ArrayListHandler ()); for (Object[] arr : list) { System.out.println(Arrays.toString(arr)); } }
查询一条记录(使用ArrayHandler) 1 2 3 4 5 6 7 8 9 10 11 @Test public void select7 () throws Exception{ QueryRunner qr = new QueryRunner (C3P0Utils.getDataSource()); String sql = "select * from user where id = ?" ; Object[] arr = qr.query(sql, new ArrayHandler (), 3 ); System.out.println(Arrays.toString(arr)); }
小结
步骤
创建QueryRunner() 对象传入DataSource
调用query(sql,ResultSetHandler, params…)
ResultSetHandler
BeanHandler() 查询一条记录封装到JavaBean对象
BeanListHandler() 查询多条记录封装到List<JavaBean>
list
ScalarHandler() 封装单个记录的 eg:统计数量
ColumnListHandler() 封装单列多行记录
MapHandler() 查询一条记录封装到Map对象
MapListHandler() 查询多条记录封装到List<Map>
list
ArrayHandler() 查询一条记录封装到数组中
原理: 使用了反射+内省
注意实现
封装到JavaBean条件, 查询出来的数据的列名必须和JavaBean属性一致
自定义DBUtils 元数据 能够说出什么是数据库元数据
我们要自定义DBUtils, 就需要知道列名, 参数个数等, 这些可以通过数据库的元数据库进行获得.元数据在建立框架和架构方面是特别重要的知识,我们可以使用数据库的元数据来创建自定义JDBC工具包, 模仿DBUtils.
什么是元数据 元数据(MetaData),即定义数据的数据。打个比方,就好像我们要想搜索一首歌(歌本身是数据),而我们可以通过歌名,作者,专辑等信息来搜索,那么这些歌名,作者,专辑等等就是这首歌的元数据。因此数据库的元数据就是一些注明数据库信息的数据。
歌曲:凉凉
作词:刘畅
演唱: 杨宗纬 张碧晨
时长: 3分28秒
…
简单来说: 数据库的元数据就是 数据库、表、列的定义信息。
① 由PreparedStatement对象的getParameterMetaData ()方法获取的是ParameterMetaData对象。
② 由ResultSet对象的getMetaData()方法获取的是ResultSetMetaData对象。
概述 ParameterMetaData是由preparedStatement对象通过getParameterMetaData方法获取而来,ParameterMetaData
可用于获取有关PreparedStatement
对象和其预编译sql语句
中的一些信息. eg:参数个数,获取指定位置占位符的SQL类型
获得ParameterMetaData:
`ParameterMetaData parameterMetaData = preparedStatement.getParameterMetaData ()`
int getParameterCount(); 获得参数个数
int getParameterType(int param) 获取指定参数的SQL类型。 (注:MySQL不支持获取参数类型)
实例代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public class Test1_ 参数元数据 { public static void main (String[] args) throws Exception{ Connection connection = C3P0Utils.getConnection(); String sql = "select * from user where username = ? and password = ?" ; PreparedStatement ps = connection.prepareStatement(sql); ParameterMetaData pmd = ps.getParameterMetaData(); int count = pmd.getParameterCount(); System.out.println("sql语句的参数个数:" +count); } }
概述 ResultSetMetaData是由ResultSet对象通过getMetaData方法获取而来,ResultSetMetaData
可用于获取有关ResultSet
对象中列的类型和属性的信息。
获得ResultSetMetaData:
`ResultSetMetaData resultSetMetaData = resultSet.getMetaData()`
getColumnCount(); 获取结果集中列项目的个数
getColumnName(int column); 获得数据指定列的列名
getColumnTypeName();获取指定列的SQL类型
getColumnClassName();获取指定列SQL类型对应于Java的类型
实例代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 public class Test2_ 结果集元数据 { public static void main (String[] args) throws Exception { Connection connection = C3P0Utils.getConnection(); String sql = "select * from user where username = ? and password = ?" ; PreparedStatement ps = connection.prepareStatement(sql); ps.setString(1 , "zs" ); ps.setString(2 , "123456" ); ResultSet resultSet = ps.executeQuery(); ResultSetMetaData rsmd = resultSet.getMetaData(); int columnCount = rsmd.getColumnCount(); System.out.println("列的个数:" + columnCount); for (int i = 1 ; i <= columnCount; i++) { System.out.println(rsmd.getColumnName(i)); } System.out.println("==================================" ); for (int i = 1 ; i <= columnCount; i++) { System.out.println(rsmd.getColumnTypeName(i)); } System.out.println("==================================" ); for (int i = 1 ; i <= columnCount; i++) { System.out.println(rsmd.getColumnClassName(i)); } } }
小结
元数据: 描述数据的数据. mysql元数据: 用来定义数据库, 表 ,列信息的 eg: 参数的个数, 列的个数, 列的类型…
mysql元数据:
==ParameterMetaData==
ResultSetMetaData
自定义DBUtils增删改 模仿DBUtils, 完成增删改的功能
创建MyQueryRunner类, 定义dataSource, 提供有参构造方法
定义int update(String sql, Object…params)方法
1 2 3 4 5 6 7 //0.非空判断 //1.从dataSource里面获得connection //2.根据sql语句创建预编译sql语句对象 //3.获得参数元数据对象, 获得参数的个数 //4.遍历, 从params取出值, 依次给参数? 赋值 //5.执行 //6.释放资源
实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 public class MyQueryRunner { private DataSource dataSource; public MyQueryRunner (DataSource dataSource) { this .dataSource = dataSource; } public MyQueryRunner () { } public int update (String sql,Object... args) throws Exception{ if (dataSource == null ){ throw new RuntimeException ("连接池不能为null" ); } if (sql == null ){ throw new RuntimeException ("sql语句不能为空" ); } Connection connection = dataSource.getConnection(); PreparedStatement ps = connection.prepareStatement(sql); ParameterMetaData pmd = ps.getParameterMetaData(); int count = pmd.getParameterCount(); for (int i = 0 ; i < count; i++) { ps.setObject(i+1 ,args[i]); } int rows = ps.executeUpdate(); C3P0Utils.release(null ,ps,connection); return rows; } }
小结
先模拟DBUtils写出架子
update()
封装了PrepareStatement操作
用到了参数元数据
Servlet Servlet入门(最重要的东西) 实操-使用IDEA创建web工程配置tomcat 能够在IDEA配置tomcat 并且创建web工程
配置tomcat 我们要将idea和tomcat集成到一起,可以通过idea就控制tomcat的启动和关闭:
创建JavaWeb工程 web工程创建
发布
情况一: 已经关联成功的情况(因为我创建项目的时候, 已经选择了tomcat)
情况二: 如果之前没有选择tomcat, 现在就需要自己关联发布
先看笔记 配置发布一下, 如果看笔记配置不出来, 看视频 ( 自己操作<=30分钟), 问同桌, 老师
Servlet概述 什么是Servlet Servlet **运行在服务端(tomcat)**的Java小程序(对象),是sun公司提供一套规范. 就是动态资源
Servlet作用 用来接收、处理客户端请求、响应给浏览器的动态资源。
但servlet的实质就是java代码,通过java的API动态的向客户端输出内容
servlet与普通的java程序的区别
必须实现servlet接口
必须在servlet容器(服务器 tomcat)中运行
servlet程序可以接收用户请求参数以及向浏览器输出数据
小结
Servlet是运行在服务器端java小程序, 动态资源
Servlet的作用: 接收请求,做出响应
案例-Servlet入门案例 在IDEA编写Servlet,发布到Tomcat. 在浏览器输入路径请求, 控制台打印Hello…
配置文件方式实现
在com.itheima.web包下创建一个类实现Servlet接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 package com.nbchen.web;import javax.servlet.*;import java.io.IOException;public class ServletDemo01 implements Servlet { @Override public void service (ServletRequest request, ServletResponse response) throws ServletException, IOException { System.out.println("hello world..." ); } @Override public void init (ServletConfig servletConfig) throws ServletException { } @Override public ServletConfig getServletConfig () { return null ; } @Override public String getServletInfo () { return null ; } @Override public void destroy () { } }
web.xml配置(该文件在web/WEB-INF 文件夹下):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?xml version="1.0" encoding="UTF-8" ?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5" > <!--配置Servlet--> <servlet> <!--servlet-name: 名字 随便取; 一般就是类名--> <servlet-name>ServletDemo01</servlet-name> <!--servlet-class:Servlet这个类的全限定名--> <servlet-class>com.itheima.web.ServletDemo01</servlet-class> </servlet> <servlet-mapping> <!--servlet-name: 必须和servlet标签里面的servlet-name一样--> <servlet-name>ServletDemo01</servlet-name> <!--url-pattern: 配置访问的路径--> <url-pattern>/demo01</url-pattern> </servlet-mapping> </web-app>
注解方式实现
在com.itheima.web包下创建一个类实现Servlet接口, 添加注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 package com.nbchen.web;import javax.servlet.*;import javax.servlet.annotation.WebServlet;import java.io.IOException;@WebServlet("/demo02") public class ServletDemo02 implements Servlet { @Override public void service (ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { System.out.println("hello world..." ); } @Override public void init (ServletConfig servletConfig) throws ServletException { } @Override public ServletConfig getServletConfig () { return null ; } @Override public String getServletInfo () { return null ; } @Override public void destroy () { } }
浏览器地址栏输入:http://localhost:8080/day27_servlet/demo02
小结 如果出现实现Servlet报错
配置文件方式与注解方式比较 注解方式简化的javaweb代码开发,可以省略web.xml配置文件.
但是配置文件方式必须掌握的(后面在框架或者大项目里面会使用到的)
步骤回顾
xml方式
创建一个类实现Servlet接口
在web.xml配置servlet
注解方式
创建一个类实现Servlet接口
在类上面添加@WebServlet(“访问的路径”)
入门案例原理和路径 Servlet执行原理
通过上述流程图我们重点需要掌握如下几个点:
Servlet对象是由服务器创建(反射)
request与response对象也是由tomcat服务器创建
service()方法也是服务器调用的
Servlet路径的配置url-pattern url-pattern配置方式共有三种:
完全路径匹配: 以 / 开始 . 注: 访问的路径不能多一个字母也不能少一个
1 例如: 配置了/demo01 请求的时候必须是: /demo01
目录匹配”以 / 开始需要以 * 结束 . 注: Servlet里面用的 不多, 但是过滤器里面通常就使用目录匹配
1 例如: 配置/* 访问/a, /aa, /aaa; 配置 /aa/* 访问 /aa/b , /aa/cc
1 例如: *.action; 访问: aa.action, bb.action, c.action; 错误写法: /*.do, 不可以写*.jsp,*.html
注意的地方:
小结
讲Servlet原理目的让大家对Servlet执行有一个深入认识, 只需要能懂就可以. 具体操作是服务器(创建servlet,执行service), 你【重点】要做的就是写出Servlet
路径有多种方式, 一般用完全路径匹配
Servlet进阶 Servlet的生命周期 生命周期概述 一个对象从创建到销毁的过程
Servlet生命周期方法 servlet从创建到销毁的过程
出生:(初始化)用户第一次访问时执行。 init(ServletConfig config)
活着:(服务)应用活着。每次访问都会执行。 service(ServletRequest req, ServletResponse res)
死亡:(销毁)应用卸载。 destroy()
Servlet生命周期描述
常规【重点】
默认情况下, 来了第一次请求, 会调用init()方法进行初始化【调用一次】
任何一次请求 都会调用service()方法处理这个请求
服务器正常关闭或者项目从服务器移除, 调用destory()方法进行销毁【调用一次】
扩展
servlet是单例多线程的, 尽量不要在servlet里面使用值可变的全局(成员)变量,可能会导致线程不安全
单例: 只有一个对象(init()调用一次, 创建一次)
多线程: 服务器会针对每次请求, 开启一个线程调用service()方法处理这个请求
在goGet
ServletConfig【了解】 Servlet的配置对象, 可以使用用ServletConfig来获得Servlet的初始化参数,在SpringMVC里面会遇到
启动项 Servlet默认情况下什么是创建? 默认情况下是第一次请求的时候.
如果我想让Servlet提前创建(服务器启动的时候), 这个时候就可以使用启动项 在SpringMVC里面会遇到
小结
Servlet生命周期方法
init() 初始化
service() 服务
distory() 销毁
Servlet生命周期描述【面试题】
默认情况下, 第一次请求的时候, 调用init()方法进行初始化【调用一次】
任何一次请求, 都会调用service()方法进行处理这个请求
服务器正常关闭/项目从服务器移除, 调用destory()方法进行销毁【调用一次】
Servlet是单例多线程的
Servlet体系结构(重点) 掌握Servlet的继承关系,能够使用IDEA直接创建Servlet
Servlet接口 前面我们已经学会创建一个类实现sevlet接口的方式开发Servlet程序,实现Servlet接口的时候,我们必须实现接口的所有方法。但是,在servlet中,真正执行程序逻辑的是service,对于servlet的初始化和销毁,由服务器调用执行,开发者本身不需要关心。因此,有没有一种更加简洁的方式来开发servlet程序呢?
我们先来查阅API回顾Servlet接口:
由上图可知在servlet接口规范下,官方推荐使用继承的方式,继承GenericServlet 或者HttpServlet来实现接口,那么我们接下来再去查看一下这两个类的API:
GenericServlet 类
阅读上图API可知,GenericServlet 是一个类,它简化了servlet的开发,已经提供好了一些servlet接口所需的方法,我们开发者只需要重写service方法即可
我们来使用GenericServlet 创建servlet:
创建一个类
继承GenericServlet
重写service方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package cn.nbchen.web;import javax.servlet.GenericServlet;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.annotation.WebServlet;import java.io.IOException;@WebServlet(name = "GenericDemoServlet",urlPatterns = "/generic") public class GenericDemoServlet extends GenericServlet { @Override public void service (ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { System.out.println("GenericDemoServlet执行......." ); } }
虽然,GenericServlet已经简化了servlet开发,但是我们平时开发程序需要按照一种互联网传输数据的协议来开发程序——http协议,因此,sun公司又专门提供了HttpServlet,来适配这种协议下的开发。
HttpServlet
阅读上图的API可知,继承HttpServlet,我们需要重写doGet、doPost等方法中一个即可,根据Http不同的请求,我们需要实现相应的方法。
我们来使用HttpServlet创建servlet:
创建一个类
继承HttpServlet
重写doGet方法和doPost方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @WebServlet("/demo02") public class ServletDemo02 extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req,resp); } @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("编写很多代码处理这次post请求" ); } }
通过以上两个API阅读,同学们注意一个细节HttpServlet是GenericServlet的子类,它增强了GenericServlet一些功能,因此,在后期使用的时候,我们都是选择继承HttpServlet来开发servlet程序。
小结
继承关系
我们可以直接创建一个类继承HttpServlet, 直接在IDEA里面new Servlet
扩展(Servlet体系源码)
看HttpServlet的service()方法
补充案例:登录 流程分析
代码 login.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > 登录页面</title > </head > <body > <form action ="/LoginDemo/login" method ="post" > 用户名<input type ="text" name ="username" > <br > 密码<input type ="text" name ="password" > <br > <input type ="submit" value ="登录" > </form > </body > </html >
LoginServlet
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package com.nbchen.servlet;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 java.io.IOException;@WebServlet("/login") public class LoginServlet extends HttpServlet { @Override protected void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } @Override protected void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username = request.getParameter("username" ); String password = request.getParameter("password" ); if ("jay" .equals(username) && "123456" .equals(password)) { response.getWriter().write("login success" ); }else { response.getWriter().write("login failed" ); } } }
ServletContext ServletContext概述 servletContext概述 ServletContext: 是一个全局对象, 上下文对象.
服务器为每一个应用(项目)都创建了一个ServletContext对象。 ServletContext属于整个应用(项目)的,不局限于某个Servlet。并且整个项目有且只会有一个ServletContext对象
ServletContext作用 作为域对象存取数据,让Servlet共享(掌握)
获得文件MIME类型(文件下载)(了解)
获得全局初始化参数(了解)
获取web资源路径 ,可以将Web资源转换成字节输入流(掌握)
ServletContext的功能 作为域对象存取值【重点】
API
getAttribute(String name) ;向ServletContext对象的map取数据
setAttribute(String name, Object object) ;从ServletContext对象的map中添加数据
removeAttribute(String name) ;根据name去移除数据
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package com.nbchen.servlet;import javax.servlet.ServletContext;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 java.io.IOException;@WebServlet("/demo01") public class ServletDemo01 extends HttpServlet { @Override protected void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } @Override protected void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String name = "周杰棍" ; ServletContext servletContext = getServletContext(); servletContext.setAttribute("name" ,name); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package com.nbchen.servlet;import javax.servlet.ServletContext;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 java.io.IOException;@WebServlet("/demo02") public class ServletDemo02 extends HttpServlet { @Override protected void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } @Override protected void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ServletContext servletContext = getServletContext(); String name = (String) servletContext.getAttribute("name" ); System.out.println("在ServletDemo02中获取的name是:" + name); } }
获得文件mime-type(了解)
getMimeType(String file)
代码
1 2 3 4 5 6 7 8 9 10 11 12 protected void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {String file01 = "a.mp3" ;String file02 = "b.png" ;String mimeType01 = getServletContext().getMimeType(file01);String mimeType02 = getServletContext().getMimeType(file02);response.getWriter().print("ServletDemo05..." +mimeType01+":" +mimeType02); }
获得全局初始化参数(了解)
String getInitParameter(String name) ; //根据配置文件中的key得到value;
在web.xml配置
通过ServletContext来获得
获取web资源路径
API
String getRealPath(String path);根据资源名称得到资源的绝对路径.
getResourceAsStream(String path) ;返回制定路径文件的流
注意: filepath:直接从项目的根目录开始写
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 @WebServlet("/demo04") public class ServletDemo04 extends HttpServlet { @Override protected void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } @Override protected void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ServletContext servletContext = getServletContext(); InputStream is = servletContext.getResourceAsStream("mm.jpg" ); System.out.println(is); } }
小结
作为域对象存取数据【共享】
setAttribute(String name,Object value) 存
getAttribute(String name) 取
removeAttribute(String name) 移除
获得文件的Mime类型
getMineType(String fileName);
获得全局初始化参数
在web.xml配置
getInitParameter(String name);
获得web资源路径【已经在web目录了】
getRealPath(String file); 获得文件的绝对路径
getReSourceAsStream(String file); 获得文件流
案例-统计网站被访问的总次数 需求
思路分析
代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 package com.nbchen.servlet;import javax.servlet.ServletContext;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 java.io.IOException;@WebServlet("/count") public class CountServlet extends HttpServlet { @Override protected void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } @Override protected void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ServletContext servletContext = getServletContext(); Object count = servletContext.getAttribute("count" ); if (count == null ) { servletContext.setAttribute("count" ,1 ); }else { int number = ((int ) count) + 1 ; servletContext.setAttribute("count" ,number); } response.getWriter().write("WelCome!!!" ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package com.nbchen.servlet;import javax.servlet.ServletContext;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 java.io.IOException;@WebServlet("/show") public class ShowServlet extends HttpServlet { @Override protected void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } @Override protected void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ServletContext servletContext = getServletContext(); int count = (int ) servletContext.getAttribute("count" ); response.getWriter().print("access count is:" +count); } }
request request概述 知道什么是request以及作用
什么是request 在Servlet API中,定义了一个HttpServletRequest接口,它继承自ServletRequest接口,专门用来封装HTTP请求消息。由于HTTP请求消息分为请求行、请求头和请求体三部分,因此,在HttpServletRequest接口中定义了获取请求行、请求头和请求消息体的相关方法.
Web服务器收到客户端的http请求,会针对每一次请求,分别创建一个用于代表请求的request对象、和代表响应的response对象。
request作用
操作请求三部分(行,头,体)
请求转发
作为域对象存数据
小结
request代表请求对象. 原型是HttpServletRequest, 服务器创建好的, 以形参的方式存在doGet()/doPost()方法里面
Request作用
操作请求的三部分(行,头,体)
转发
作为域对象存取数据
操作请求行和请求头 获取客户机信息(操作请求行) 请求方式 请求路径(URI) 协议版本
GET /day17Request/WEB01/register.htm?username=zs&password=123456 HTTP/1.1
getMethod() ;获取请求方式
getRemoteAddr() ;获取客户机的IP地址(知道是谁请求的)
getContextPath() ; 获得当前应用工程名(部署的路径);
getRequestURI();获得请求地址,不带主机名
getRequestURL();获得请求地址,带主机名
getServerPort();获得服务端的端口
getQueryString();获的请求参数(get请求的,URL的?后面的. eg:username=zs&password=123456)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 @WebServlet("/demo01") public class ServletDemo01 extends HttpServlet { @Override protected void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } @Override protected void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String method = request.getMethod(); String remoteAddr = request.getRemoteAddr(); String contextPath = request.getContextPath(); String url = request.getRequestURL().toString(); String uri = request.getRequestURI(); System.out.println(uri); } }
获得请求头信息(操作请求头)(了解) 请求头: 浏览器告诉服务器自己的属性,配置的, 以key value存在, 可能一个key对应多个value
getHeader(String name);
User-Agent: 浏览器信息
Referer:来自哪个网站(防盗链)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @WebServlet("/demo02") public class ServletDemo02 extends HttpServlet { @Override protected void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } @Override protected void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String header = request.getHeader("user-agent" ); System.out.println("获取的请求头agent为:" + header); } }
小结
操作请求行(获得客户机信息)
getMethod(); 获得请求方式
getRemoteAddr(); 获得客户机的IP地址
getContextPath(); 获得项目的部署的路径
getRequestURI(); 获得URI(不带http,主机,端口)
getRequestURL(); 获得URL(带http,主机,端口)
操作请求头
getHeader(String name);
User-Agent: 浏览器信息
Referer:来自哪个网站(防盗链)
获得请求参数【重点】 获得请求参数
法名
描述
String getParameter(String name)
获得指定参数名对应的值。如果没有则返回null,如果有多个获得第一个。 例如:username=jack
String[] getParameterValues(String name)
获得指定参数名对应的所有的值。此方法专业为复选框提供的。 例如:hobby=抽烟&hobby=喝酒&hobby=敲代码
Map<String,String[]> getParameterMap()
获得所有的请求参数。key为参数名,value为key对应的所有的值。
请求参数乱码处理 我们在输入一些中文数据提交给服务器的时候,服务器解析显示出来的一堆无意义的字符,就是乱码。 那么这个乱码是如何出现的呢?如下图所示:
get方式, 我们现在使用的tomcat>=8.0了, 乱码tomcat已经处理好了
post方式, 就需要自己处理
1 void setCharacterEncoding(String env); //设置请求体的编码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 @WebServlet("/demo03") public class ServletDemo03 extends HttpServlet { @Override protected void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } @Override protected void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8" ); String username = request.getParameter("username" ); String[] hobbies = request.getParameterValues("hobby" ); Map<String, String[]> parameterMap = request.getParameterMap(); Set<Map.Entry<String, String[]>> entries = parameterMap.entrySet(); for (Map.Entry<String, String[]> entry : entries) { String parameterName = entry.getKey(); String[] values = entry.getValue(); for (String value : values) { System.out.println("参数名:" + parameterName + ",参数值:" + value); } } } }
使用BeanUtils封装 现在我们已经可以使用request对象来获取请求参数,但是,如果参数过多,我们就需要将数据封装到对象。
以前封装数据的时候,实体类有多少个字段,我们就需要手动编码调用多少次setXXX方法,因此,我们需要BeanUtils来解决这个问题。
BeanUtils是Apache Commons组件的成员之一,主要用于简化JavaBean封装数据的操作。
使用步骤:
导入jar
使用BeanUtils.populate(user,map)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > 注册页面</title > </head > <body > <form action ="/requestDemo/demo04" method ="post" > 用户名<input type ="text" name ="username" > <br > 密码<input type ="text" name ="password" > <br > 性别<input type ="radio" name ="gender" value ="male" > 男 <input type ="radio" name ="gender" value ="female" > 女 <br > 兴趣爱好 <input type ="checkbox" name ="hobby" value ="basketball" > 篮球 <input type ="checkbox" name ="hobby" value ="football" > 足球 <input type ="checkbox" name ="hobby" value ="ppball" > 乒乓球 <input type ="checkbox" name ="hobby" value ="yumaoball" > 羽毛球 <br > <input type ="submit" value ="注册" > </form > </body > </html >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 public class User implements Serializable { private String username; private String password; private String gender; private String[] hobby; public User () { } public User (String username, String password, String gender, String[] hobby) { this .username = username; this .password = password; this .gender = gender; this .hobby = hobby; } @Override public String toString () { return "User{" + "username='" + username + '\'' + ", password='" + password + '\'' + ", gender='" + gender + '\'' + ", hobby=" + Arrays.toString(hobby) + '}' ; } 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; } public String getGender () { return gender; } public void setGender (String gender) { this .gender = gender; } public String[] getHobby() { return hobby; } public void setHobby (String[] hobby) { this .hobby = hobby; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 @WebServlet("/demo04") public class ServletDemo04 extends HttpServlet { @Override protected void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } @Override protected void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8" ); Map<String, String[]> parameterMap = request.getParameterMap(); User user = new User (); try { BeanUtils.populate(user,parameterMap); System.out.println(user); } catch (Exception e) { e.printStackTrace(); } } }
小结
获取请求参数的方法
getParameter(name),根据参数名获取一个参数值
getParameterValues(name),根据参数名获取多个参数值
getParameterMap(),获取所有的请求参数封装到map中
使用BeanUtils封装
如果请求参数有多个需要封装到JavaBean里面, 建议先获得Map, 再使用BeanUtils封装到JavaBean对象
注意: JavaBean属性需要和Map的key一致 说白了也就是JavaBean属性需要和表单的name一致
解决请求参数的中文乱码问题
Tomcat8之后,使用get方式提交的请求参数不会发生中文乱码
解决post请求中的中文参数乱码问题:在获取请求参数之前添加一句代码:request.setCharacterEncoding(“UTF-8”)
请求转发【重点】 掌握请求转发
1 request.getRequestDispatcher(url).forward(request, response);//转发
小结
请求转发的作用:跳转页面,比如说添加完数据之后跳转到数据的展示页面,删除完数据之后跳转到展示页面
1 request.getRequestDispatcher("转发的路径" ).forward(request,response);
请求转发的特征
跳转操作是由服务器执行的,所以客户端地址栏不会发生变化
跳转操作不会发起新的请求
可以跳转到WEB-INF中的资源,但是不能跳转到其它项目的资源
作为域对象存取值 目标
讲解 ServletContext: 范围 整个应用(无论多少次请求,只要是这个应用里面的都是可以==共享==的)
request范围: 一次请求有效
域对象是一个容器,这种容器主要用于Servlet与Servlet/JSP之间的数据传输使用的。
Object getAttribute(String name) ;
void setAttribute(String name,Object object) ;
void removeAttribute(String name) ;
小结
作为域对象存取数据
Object getAttribute(String name) ; 取
void setAttribute(String name,Object object) ; 存
void removeAttribute(String name) ; 移除
范围: 一次请求有效(转发可以使用)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 @WebServlet("/demo06") public class ServletDemo06 extends HttpServlet { @Override protected void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } @Override protected void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username = "周杰棍" ; request.setAttribute("name" ,username); request.getRequestDispatcher("/demo07" ).forward(request, response); } }
ServletDemo07的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @WebServlet("/demo07") public class ServletDemo07 extends HttpServlet { @Override protected void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } @Override protected void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("在ServletDemo07中获取username的值:" +request.getAttribute("name" )); } }
Request部分的小结
获取请求行的信息
getMethod()获取请求方式
getRequestURI()获取请求的uri地址
getRequestURL()获取请求的url地址
getContextPath()获取项目的部署路径
getRemoteAddr()获取客户端的IP地址
获取请求头的信息:getHeader(name) 掌握
获取请求参数(全部要掌握,最重要)
getParameter(参数名),根据一个参数名获取一个参数值
getParameterValues(参数名),根据一个参数名获取多个参数值
getParameterMap()获取所有的请求参数,封装到Map中
解决请求参数乱码问题: request.setCharacterEncoding(“UTF-8”);
使用BeanUtils将map中的数据存储到JavaBean对象中(掌握)
拷贝BeanUtils的jar包,存放在WEB-INF里面的lib中
map的key要和JavaBean的属性名保持一致,如果不一致那么该字段的值就无法存储
BeanUtils中默认内置一些基本类型的转换器(如果map中的数据是string类型,JavaBean的属性还是int类型那么会自动转换)
使用request做请求转发: request.getRequestDispatcher(“跳转路径”).forward(request,response); 掌握
浏览器只会发送一次请求,而且浏览器的地址是跳转之前的路径
跳转操作由服务器完成的
request对象作为域对象向存取数据,它的作用范围是一次请求中,和请求转发一起使用 掌握
setAttribute(name,value)往域对象中存数据
getAttribute(name)从域对象中获取数据
Response Response概述 HttpServletResponse概述 在Servlet API中,定义了一个HttpServletResponse接口(doGet,doPost方法的参数),它继承自ServletResponse接口,专门用来封装HTTP响应消息。由于HTTP响应消息分为响应行、响应头、响应体三部分,因此,在HttpServletResponse接口中定义了向客户端发送响应状态码、响应头、响应体的方法
作用
小结
Response代表响应对象. 原型是HttpServletResponse, 服务器创建的, 以形参的形式存在doGet()/doPost()方法
Response的作用
操作响应行
常用的状态码:
200:成功
302:重定向
304:访问缓存
404:请求路径错误
500:服务器错误
小结
设置的API: response.setStatus(int code);
一般不需要设置, 可能302 重定向需要设置
常见的响应状态码
200 成功
302 重定向
304 读缓存
404 客户端错误
500 服务器错误
操作响应头
操作响应头的API 响应头: 是服务器指示浏览器去做什么
一个key对应一个value
一个key对应多个value
关注的方法: setHeader(String name,String value);
常用的响应头
Refresh: 定时跳转 (eg:服务器告诉浏览器5s之后跳转到百度)
Location:重定向地址(eg: 服务器告诉浏览器跳转到xxx)
Content-Disposition: 告诉浏览器下载
Content-Type:设置响应内容的MIME类型(服务器告诉浏览器内容的类型)
定时刷新 1 response.setHeader("refresh" ,"秒数;url=跳转的路径" );
重定向【重点】
重定向两次请求
重定向的地址栏路径改变
重定向的路径写绝对路径(带域名/ip地址的, 如果是同一个项目里面的,域名/ip地址可以省略)
重定向的路径可以是项目内部的,也可以是项目以外的(eg:百度)
重定向不能重定向到WEB-INF下的资源
把数据存到request里面, 重定向不可用
1 2 3 4 5 6 7 8 9 10 response.sendRedirect("http://localhost:8080/day28/demo08" );
1 response.sendRedirect("重定向的路径" );
重定向和请求转发的对比
重定向的特点
重定向的跳转是由浏览器发起的,在这个过程中浏览器会发起两次请求
重定向跳转可以跳转到任意服务器的资源,但是无法跳转到WEB-INF中的资源
重定向跳转不能和request域对象一起使用
重定向跳转浏览器的地址栏中的地址会变成跳转到的路径
请求转发的特点
请求转发的跳转是由服务器发起的,在这个过程中浏览器只会发起一次请求
请求转发只能跳转到本项目的资源,但是可以跳转到WEB-INF中的资源
请求转发可以和request域对象一起使用
操作响应体 操作响应体的API
页面输出只能使用其中的一个流实现,两个流是互斥的.
使用字符输出流输出字符串
1 response.setContentType("text/html;charset=utf-8");
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 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 java.io.IOException;import java.io.PrintWriter;@WebServlet("/demo04") public class ServletDemo04 extends HttpServlet { @Override protected void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } @Override protected void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8" ); PrintWriter writer = response.getWriter(); writer.write("你好世界" ); } }
使用字节输出流向浏览器输出文件 目标是:向浏览器输出一张图片
注意:需要引入commons-io的jar包
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 @WebServlet("/demo05") public class ServletDemo05 extends HttpServlet { @Override protected void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } @Override protected void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ServletContext servletContext = getServletContext(); InputStream is = servletContext.getResourceAsStream("b.jpg" ); ServletOutputStream os = response.getOutputStream(); IOUtils.copy(is,os); os.close(); is.close(); } }
路径问题 完整url地址 url的组成部分
协议 http://
服务器主机地址 localhost
服务器的端口号 :8080
项目的虚拟路径(部署路径) responseDemo
具体的项目上资源路径 /pages/hello.html 或者 /demo02 Servlet的映射路径
什么时候会使用完整的url
浏览器地址栏直接访问
一个项目中,访问另一个项目中的资源
相对路径 相对路径的概念 不以”/“开头的路径写法,它是以目标路径相对当前文件的路径,其中”..”表示上一级目录
它是以目标资源的url,相对当前资源的url
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > </title > </head > <body > <h1 > hello world....</h1 > <a href ="../demo05" > 访问ServletDemo05</a > </body > </html >
绝对路径 绝对路径的概念 绝对路径就是以”/“开头的路径写法,它有如下两种情况
请求转发 的绝对路径写法 “/资源的路径”,不需要写项目路径,例如”/hello.html”
不是请求转发 的绝对路径写法”/项目部署路径/资源路径” 例如 “/responseDemo/hello.html”
案例-完成文件下载 需求分析
创建文件下载的列表的页面,点击列表中的某些链接,下载文件.
文件下载分析 什么是文件下载 将服务器上已经存在的文件,输出到客户端浏览器.
说白了就是把服务器端的文件拷贝一份到客户端, 文件的拷贝—> 流(输入流和输出流)的拷贝
文件下载的方式
第一种:超链接方式(不推荐)
链接的方式:直接将服务器上的文件的路径写到href属性中.如果浏览器不支持该格式文件,那么就会提示进行下载, 如果浏览器支持这个格式(eg: png, jpg….)的文件,那么直接打开,不再下载了
第二种:手动编码方式(推荐)
手动编写代码实现下载.无论浏览器是否识别该格式的文件,都会下载.
思路分析 超链接方式
准备下载的资源(文件)
编写一个下载页面
在这个页面上定义超链接,指定href
编码方式 手动编码方式要求
设置两个头和一个流
设置的两个头:
Content-Dispostion: 服务器告诉浏览器去下载
设置一个流:
获得要下载的文件的输入流.
思路
代码实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 package com.nbchen.servlet;import org.apache.commons.io.IOUtils;import javax.servlet.ServletException;import javax.servlet.ServletOutputStream;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.io.InputStream;import java.net.URLEncoder;@WebServlet("/download") public class DownloadServlet extends HttpServlet { @Override protected void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } @Override protected void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String fileName = request.getParameter("fileName" ); String mimeType = getServletContext().getMimeType(fileName); response.setHeader("Content-Type" ,mimeType); InputStream is = getServletContext().getResourceAsStream("file/" + fileName); ServletOutputStream os = response.getOutputStream(); String encodeFileName = URLEncoder.encode(fileName, "UTF-8" ); response.setHeader("Content-Disposition" ,"attachment;filename=" +encodeFileName); IOUtils.copy(is,os); os.close(); is.close(); } }
细节处理
告诉浏览器设置的响应头里面不支持中文的, 抓包来看:
综合案例 案例-注册
三层架构(今天先不讲)
分层
包名(公司域名倒写)
表现层(web层)
com.itheima.web
业务层(service层)
com.itheima.service
持久层(数据库访问层)
com.itheima.dao
JavaBean
com.itheima.bean
工具类
com.itheima.utils
分层的意义:
解耦:降低层与层之间的耦合性。
可维护性:提高软件的可维护性,对现有的功能进行修改和更新时不会影响原有的功能。
可扩展性:提升软件的可扩展性,添加新的功能的时候不会影响到现有的功能。
可重用性:不同层之间进行功能调用时,相同的功能可以重复使用。
程序设计的宗旨:
完成注册案例 注册案例思路
准备工作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 create database day25;use day25; DROP TABLE IF EXISTS `user `;CREATE TABLE `user ` ( `id` int (11 ) NOT NULL AUTO_INCREMENT, `username` varchar (40 ) DEFAULT NULL , `password` varchar (40 ) DEFAULT NULL , `address` varchar (40 ) DEFAULT NULL , `nickname` varchar (40 ) DEFAULT NULL , `gender` varchar (10 ) DEFAULT NULL , `email` varchar (20 ) DEFAULT NULL , `status` varchar (10 ) DEFAULT NULL , PRIMARY KEY (`id`) ) ENGINE= InnoDB AUTO_INCREMENT= 8 DEFAULT CHARSET= utf8
1 2 3 4 5 6 7 8 9 10 11 public class User implements Serializable { private Integer id; private String username; private String password; private String address; private String nickname; private String gender; private String email; private String status; }
导入jar
mysql驱动
druid
dbutils
beanutils
工具类和配置文件
DruidUtil
druid.properties
注册案例实现 注册页面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > 注册页面</title > </head > <body > <form action ="/userDemo/register" method ="post" > 用户名<input type ="text" name ="username" > <br > 密码<input type ="text" name ="password" > <br > 昵称<input type ="text" name ="nickname" > <br > 地址<input type ="text" name ="address" > <br > 邮箱<input type ="text" name ="email" > <br > 性别<input type ="radio" name ="gender" value ="male" > 男 <input type ="radio" name ="gender" value ="female" > 女 <br > <input type ="submit" value ="注册" > </form > </body > </html >
RegisterServlet的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 @WebServlet("/register") public class RegisterServlet extends HttpServlet { @Override protected void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } @Override protected void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8" ); response.setContentType("text/html;charset=UTF-8" ); Map<String, String[]> parameterMap = request.getParameterMap(); User user = new User (); user.setStatus("0" ); try { BeanUtils.populate(user,parameterMap); QueryRunner queryRunner = new QueryRunner (DruidUtil.getDataSource()); String sql = "insert into user values (null,?,?,?,?,?,?,?)" ; queryRunner.update(sql,user.getUsername(),user.getPassword(),user.getAddress(), user.getNickname(),user.getGender(),user.getEmail(),user.getStatus()); response.sendRedirect("/userDemo/login.html" ); } catch (Exception e) { e.printStackTrace(); response.getWriter().write("注册失败" ); } } }
小结
注册本质: 向数据库插入一条记录
思路(在RegisterServlet)
获得用户提交的数据, 使用BeanUtils封装成User对象
补全User对象(状态)
使用DBUtils向数据库里面插入一条记录
响应
案例-登录
点击登录按钮, 进行登录.
登录成功,显示login Success
登录失败,显示login failed
思路
准备工作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > 登录页面</title > </head > <body > <form action ="/userDemo/login" method ="post" > 用户名<input type ="text" name ="username" > <br > 密码<input type ="text" name ="password" > <br > <input type ="submit" value ="登录" > </form > </body > </html >
代码实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 package com.nbchen.servlet;import com.itheima.pojo.User;import com.itheima.utils.DruidUtil;import org.apache.commons.dbutils.QueryRunner;import org.apache.commons.dbutils.handlers.BeanHandler;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 java.io.IOException;@WebServlet("/login") public class LoginServlet extends HttpServlet { @Override protected void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } @Override protected void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8" ); response.setContentType("text/html;charset=UTF-8" ); String username = request.getParameter("username" ); String password = request.getParameter("password" ); String sql = "select * from user where username=? and password=?" ; QueryRunner queryRunner = new QueryRunner (DruidUtil.getDataSource()); try { User user = queryRunner.query(sql, new BeanHandler <>(User.class), username, password); if (user != null ) { response.getWriter().write("登录成功" ); }else { response.getWriter().write("登录失败" ); } } catch (Exception e) { e.printStackTrace(); response.getWriter().write("登录失败" ); } } }
小结
本质: 就是根据用户名和密码查询数据库
思路(LoginServlet)
获得用户输入用户名和密码
使用DBUtils根据用户名和密码查询数据库 封装成User对象
判断是否登录成功(判断User是否为null)
响应
会话 会话的概念 用户打开浏览器,浏览不同的网页(资源),发出多个请求
,直到关闭浏览器的过程,称为一次会话(多次请求). 如同打电话。
我们在会话的过程(多次请求)之中,用户可能会产生一些数据,这些数据有的需要保存起来的,我们就可以通过会话技术来保存用户各自的数据。
在Servlet
技术中,提供了两个用于保存会话数据的对象,分别是Cookie和Session
。
为什么要使用会话技术 保存**用户各自(以浏览器为单位)**的数据。
常用的会话技术 cookie cookie是客户端(浏览器)端的技术 ,用户浏览的信息以键值对(key=value)的形式保存在浏览器上。如果没有关闭浏览器,再次访问服务器,会把cookie带到服务端,服务端就可以做响应的处理。
session session是服务器端的技术。 服务器为每一个浏览器开辟一块内存空间,即session。由于内存空间是每一个浏览器独享的,所有用户在访问的时候,可以把信息保存在session对象中。同时,每一个session对象都对应一个sessionId,服务器把sessionId写到cookie中,再次访问的时候,浏览器会把cookie(sessionId)带过来,找到对应的session对象。
小结
为什么要使用会话技术?
保存用户各自(浏览器为单位)的数据
常见的会话技术?
Cookie 浏览器端的技术
Session 服务器端的技术
Cookie Cookie的概念和作用 Cookie的概念
Cookie是一种客户端的会话技术,它是服务器存放在浏览器的一小份数据,浏览器以后每次访问该服务器的时候都会将这小份数据携带到服务器去。
Cookie的作用
在浏览器中存放数据
将浏览器中存放的数据携带到服务器
Cookie的应用场景 1.记住用户名 当我们在用户名的输入框中输入完用户名后,浏览器记录用户名,下一次再访问登录页面时,用户名自动填充到用户名的输入框.
2.自动登录(记住用户名和密码) 当用户在淘宝网站登录成功后,浏览器会记录登录成功的用户名和密码,下次再访问该网站时,自动完成登录功能. 以上这些场景都是使用会话cookie实现的,将上次的信息保存到了cookie中,下次直接从cookie中获取数据信息
3.保存电影的播放进度
在网页上播放电影的时候,如果中途退出浏览器了,下载再打开浏览器播放同一部电影的时候,会自动跳转到上次退出时候的进度,因为在播放的时候会将播放进度保存到cookie中
Cookie就是将一些少量的配置信息保存在”浏览器”
小结
Cookie的快速入门 相关的API
创建一个Cookie对象(cookie只能保存字符串数据。且不能保存中文)
1 new Cookie(String name,String value);
1 response.addCookie(cookie);
1 request.getCookies() ; //得到所有的cookie对象。是一个数组,开发中根据key得到目标cookie
1 2 cookie.getName() ; //返回cookie中设置的key cookie.getValue(); //返回cookie中设置的value
入门代码 ServletDemo01
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @WebServlet("/demo01") public class ServletDemo01 extends HttpServlet { @Override protected void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } @Override protected void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String str = "周杰棍" ; Cookie cookie = new Cookie ("username" ,str); response.addCookie(cookie); } }
ServletDemo02
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 @WebServlet("/demo02") public class ServletDemo02 extends HttpServlet { @Override protected void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } @Override protected void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Cookie[] cookies = request.getCookies(); if (cookies != null ) { for (Cookie cookie : cookies) { if (cookie.getName().equals("username" )) { String value = cookie.getValue(); System.out.println("在ServletDemo02中获取str的值为:" + value); } } } } }
小结
cookie特点
Cookie保存在客户端(浏览器端的)
第一次请求的时候, 没有Cookie的, 先由服务器写给浏览器.
Cookie里面只能保存字符串, 大小有限制
cookie相关API
new Cookie(String name,String value); 创建Cookie
response.addCookie(cookie); 把Cookie写给浏览器
request.getCookies(); 获得所有的Cookie对象
cookie.getName() 获得Cookie的key
cookie.getValue() 获得Cookie的value
Cookie本质是请求头,响应头
Cookie进阶 cookie的分类
在默认的情况 下,当浏览器进程结束(浏览器关闭,会话结束)的时候,cookie就会消失。
持久性cookie
给cookie设置有效期.cookie.setMaxAge(int expiry)
:时间是秒
-1:默认。代表Cookie数据存到浏览器关闭(保存在浏览器文件中)。
正整数:以秒为单位保存数据有有效时间(把缓存数据保存到磁盘中)
0:代表删除Cookie.如果要删除Cookie要确保路径一致 。
cookie设置有效路径 1 setPath(String url) ;设置路径
有效路径作用 :
保证不会携带别的网站/项目里面的cookie到我们自己的项目
如果路径不一样, cookie的key可以相同
保证自己的项目可以合理的利用自己项目的cookie
默认路径,例如:
随带Cookie需要的条件: ==只有当访问资源的url包含此cookie的有效path的时候,才会携带这个cookie;==反之不会.
cookie的路径通常设置 / 或者 /发布项目名设置的有效是 /day30a-cookie. 当前项目下的Servlet都可以使用该cookie. 一般这么设置: cookie.setPath(request.getContextPath() );
只要是当前项目里面的资源 路径必须包含项目名路径.
小结
Cookie的类型
会话级别【默认的】 浏览器关闭了就消失了
持久级别 setMaxAge(int 秒)
-1 默认值
正整数
0 删除cookie 【必须路径一致】
cookie有效路径 cookie.setPath(String path) 建议设置成当前的项目部署路径; setPath(request.getContextPath())
cookie的弊端 cookie的大小(个数和自身大小)和格式(只能存字符串)有限制,默认不支持中文,解决中文办法
具体和版本有关,我们在实际项目中,如果要使用cookie的话,一般是不会存储中文的,万一需要存储中文
1 2 URLEncoder.encode(value,"utf-8");//存入的时候(先通过utf-8编码) URLDecoder.decode(value,"utf-8");//取出 (通过utf-8解码)
补充-封装cookie的工具类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 public class CookieUtil { public static Cookie createAndSetCookie (String name,String value,int time,String path) { Cookie cookie = new Cookie (name,value); cookie.setMaxAge(time); cookie.setPath(path); return cookie; } public static String getCookieValue (Cookie[] cookies,String name) { String value = null ; if (cookies != null ) { for (Cookie cookie : cookies) { if (cookie.getName().equals(name)) { value = cookie.getValue(); } } } return value; } }
案例-记录用户各自的上次访问时间 需求
在访问一个资源的时候,展示上次访问的时间
若是第一次访问则展示:你是第一次访问,若不是第一次则展示:你上次访问的时间是:xxxx
分析
代码实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 package com.nbchen.servlet;import com.nbchen.utils.CookieUtil;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.Cookie;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.text.SimpleDateFormat;import java.util.Date;@WebServlet("/rem") public class RememberServlet extends HttpServlet { @Override protected void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } @Override protected void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8" ); Cookie[] cookies = request.getCookies(); String lastTime = CookieUtil.getCookieValue(cookies, "lastTime" ); if (lastTime == null ) { response.getWriter().write("你是第一次访问!!!" ); }else { response.getWriter().write("您的上次访问时间是:" +lastTime); } lastTime = new SimpleDateFormat ("yyyy-MM-dd/HH:mm:ss" ).format(new Date ()); Cookie cookie = CookieUtil.createAndSetCookie("lastTime" , lastTime, 7 * 24 * 60 * 60 , request.getContextPath()); response.addCookie(cookie); } }
小结
案例关键: 判断是否第一次访问【说白了就是判断目标Cookie是否为null】
取的时候 和存时候的key要一致
不管是哪一次访问, 都需要记录当前的时间到Cookie
Session session概述 session概述 session是服务器端的技术。服务器为每一个浏览器开辟一块内存空间,即session对象。由于session对象是每一个浏览器特有的,所以用户的记录可以存放在session对象中。同时,每一个session对象都对应一个sessionId,服务器把sessionId写到cookie中,再次访问的时候,浏览器把sessionId带过来,找到对应的session对象
cookie和Session的不同
cookie是保存在浏览器端的,大小和个数都有限制。session是保存在服务器端的, 原则上大小是没有限制(实际开发里面也不会存很大大小), 安全一些。
cookie不支持中文,并且只能存储字符串;session可以存储基本数据类型,集合,对象等
Session的执行原理 1、获得cookie中传递过来的SessionId(cookie)
2、如果Cookie中没有sessionid,则创建session对象
3、如果Cookie中有sessionid,找指定的session对象
如果有sessionid并且session对象存在,则直接使用
如果有sessionid,但session对象销毁了,则执行第二步
小结
session是服务器端的技术, 数据保存在服务器端的
只有在服务器端调用了requet.getSession()的时候, 才有session产生
session基于cookie的
创建session的同时 生成sessionId, 服务器自动通过Cookie的方式写给浏览器, 浏览器自己保存
下次的话 浏览器携带cookie(SessionId)找到对应的session使用了
Session的基本使用 范围: 会话(多次请求) 保存用户各自的数据(以浏览器为单位)
request.getSession(); 获得session(如果第一次调用的时候其实是创建session,第一次之后通过sessionId找到session进行使用)
Object getAttribute(String name) ;获取值
void setAttribute(String name, Object value) ;存储值
void removeAttribute(String name) ;移除
小结
Session基本使用: 作为域对象存取数据 范围: **一次会话(多次请求, 用户各自的)**不同的浏览器session不一样
Object getAttribute(String name) ;获取值
void setAttribute(String name, Object value) ;存储值
void removeAttribute(String name) ;移除
浏览器关闭了, session使用不了, 是session销毁了吗?
session没有销毁.
session基于cookie, sessionId保存到cookie里面的, 默认情况下cookie是会话级别,浏览器关闭了cookie就是消失了,也就是说sessionId消失了 , 从而找不到对应的session对象了, 就不能使用了.
解决: 自己获得sessionId, 自己写给浏览器 设置Cookie的有效时长, 这个Cookie的key必须: JSESSIONID
三个域对象比较 三个域对象比较
域对象
创建
销毁
作用范围
应用场景
ServletContext
服务器启动
服务器正常关闭/项目从服务器移除
整个项目
记录网站访问次数,聊天室
HttpSession
没有JSESSIONID这个cookie的时候,调 用request.getSession()方法
session过期(默认闲置30分钟),或者调用session对象的invalidate()方法,或者服务器异常 关闭
会话(多次请求)
验证码校验, 保存用户登录状态 等
HttpServletRequest
来了请求
响应这个请求(或者请求已经接收了)
一次请求
servletA和jsp(servletB)之间数据传递(转发的时候存数据)
C:\Users\用户名\.IntelliJIdea2017.2\system\tomcat\_sz61\work\Catalina\localhost
目录查看
把session钝化到服务器磁盘上,再次启动,把磁盘上的文件活化到内存里面
Session钝化:把内存中的session序列化保存到硬盘上
Session活化:从硬盘上读取序列化的session到内存中形成一个session对象
三个域对象怎么选择? 三个域对象怎么选择?
一般情况下, 最小的可以解决就用最小的.
但是需要根据情况(eg: 重定向, 多次请求, 会话范围, 用session; 如果是转发,一般选择request)
小结
session的销毁
session应用场景
选择
一般情况下, 最小的可以解决就用最小的.
转发 一般选择request
重定向 一般选择session
案例-一次性验证码校验 需求
在网站登录的时候,生成一个验证码.登录的时候对验证码进行校验.
分析 生成验证码
拷贝验证码的jar包
创建CodeServlet
1 2 //1.生成验证码 //2.响应给客户端(浏览器)
校验验证码
实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > </head > <body > <center > <h1 > 用户登录</h1 > <form action ="login" method ="post" > 姓名:<input type ="text" name ="username" /> <br /> 密码:<input type ="password" name ="password" /> <br /> 验证码:<input type ="text" name ="checkCode" > <br > <img src ="checkCode" id ="code" > <a href ="javascript:;" onclick ="changeCheckCode()" > 换一换</a > <br > <input type ="submit" value ="登录" /> </form > <script > function changeCheckCode ( ) { document .getElementById ("code" ).setAttribute ("src" ,"checkCode?date=" +new Date ()) } </script > </center > </body > </html >
CheckCodeServlet(需要引入jar包)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package com.nbchen.servlet;import cn.dsna.util.images.ValidateCode;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 java.io.IOException;@WebServlet("/checkCode") public class CheckCodeServlet extends HttpServlet { @Override protected void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } @Override protected void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ValidateCode validateCode = new ValidateCode (100 , 30 , 4 , 20 ); String code = validateCode.getCode(); request.getSession().setAttribute("code" ,code); validateCode.write(response.getOutputStream()); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 package com.nbchen.servlet;import com.nbchen.pojo.User;import com.nbchen.utils.DruidUtil;import org.apache.commons.dbutils.QueryRunner;import org.apache.commons.dbutils.handlers.BeanHandler;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 javax.servlet.http.HttpSession;import java.io.IOException;@WebServlet("/login") public class LoginServlet extends HttpServlet { @Override protected void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } @Override protected void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { request.setCharacterEncoding("UTF-8" ); response.setContentType("text/html;charset=UTF-8" ); String username = request.getParameter("username" ); String password = request.getParameter("password" ); String checkCode = request.getParameter("checkCode" ); HttpSession session = request.getSession(); String code = (String) session.getAttribute("code" ); if (code.equalsIgnoreCase(checkCode)) { QueryRunner queryRunner = new QueryRunner (DruidUtil.getDataSource()); String sql = "select * from user where username=? and password=?" ; User user = queryRunner.query(sql, new BeanHandler <>(User.class), username, password); if (user != null ) { response.sendRedirect("/userDemo/success.html" ); }else { response.getWriter().write("用户名或密码错误" ); } }else { response.getWriter().write("验证码错误" ); } } catch (Exception e) { e.printStackTrace(); response.getWriter().write("登陆失败" ); } } }
小结
需要在CodeServlet 把生成的验证码存到session里面
不能存到ServletContext, 就共享验证码了
不能存到request, 根本不能用
思路
CodeServlet: 生成验证码存到Session
LoginServlet:
获得用户输入的验证码
获得session里面存的验证码
比较是否一致
Filter Filter概述 什么是filter Filter:一个实现了特殊接口(Filter)的Java类. 实现对请求资源(jsp,servlet,html,)的过滤的功能.
过滤器是一个运行在服务器的程序, 优先于请求资源(Servlet或者jsp,html)之前执行. 过滤器是javaweb技术中最为实用 的技术之一.
过滤的作用 对目标资源(Servlet,jsp)进行过滤.
应用场景:登录权限检查,解决网站乱码,过滤敏感字符 …
小结
Filter是运行在服务器端的Java程序, 优先于请求资源(jsp,servlet,html…)之前执行
FIlter应用场景
….
Filter入门(重点) 配置文件方式
创建一个类实现Filter接口
在web.xml配置FIlter的拦截路径
注解方式
创建一个类实现Filter接口
在这个类上面添加@WebFilter(“拦截的路径”)
建议:可以直接new Filter
代码 通过xml配置方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class FilterDemo01 implements Filter { @Override public void doFilter (ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("FilterDemo01收到了请求..." ); } @Override public void init (FilterConfig filterConfig) throws ServletException { } @Override public void destroy () { } }
1 2 3 4 5 6 7 8 9 10 <filter > <filter-name > FilterDemo01</filter-name > <filter-class > com.itheima.web.filter.FilterDemo01</filter-class > </filter > <filter-mapping > <filter-name > FilterDemo01</filter-name > <url-pattern > /demo01</url-pattern > </filter-mapping >
通过注解方式
创建一个类实现Filter接口
直接在这个类上面添加注解进行配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @WebFilter("/demo02") public class FilterDemo02 implements Filter { @Override public void doFilter (ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("FilterDemo02... 收到了请求" ); } @Override public void init (FilterConfig filterConfig) throws ServletException { } @Override public void destroy () { } }
小结 入门步骤
xml方式
创建一个类实现Filter接口
在web.xml配置Filter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <filter > <filter-name > FilterDemo01</filter-name > <filter-class > com.itheima.web.filter.FilterDemo01</filter-class > </filter > <filter-mapping > <filter-name > FilterDemo01</filter-name > <url-pattern > /demo01</url-pattern > </filter-mapping >
注解方式
创建一个类实现Filter接口
在这个类上面添加@WebFilter(“拦截的路径”)
request和response
Filter的生命周期 Filter生命周期介绍 过滤器从创建到销毁的过程
生命周期方法 init(FilterConfig):初始化
doFilter(ServletReqeust req,ServletResponse resp,FilterChain chain):执行过滤的方法
destroy():销毁
Filter生命周期描述
服务器启动的时候, 会调用init()方法进行初始化【调用一次】
任何一次请求都会调用doFilter()方法进行过滤【路径相匹配】
服务器正常关闭或者项目从服务器移除, 调用destory()方法进行销毁【调用一次】
注意: 默认情况下,Servlet是来了第一次请求的时候 调用init()方法进行初始化.我们可以在Servlet里面设置启动项.
FilterConfig【了解】 获得过滤器的初始化参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <filter > <filter-name > myFilter01</filter-name > <filter-class > com.nbchen.filter.MyFilter01</filter-class > <init-param > <param-name > encoding</param-name > <param-value > UTF-8</param-value > </init-param > </filter > <filter-mapping > <filter-name > myFilter01</filter-name > <url-pattern > /*</url-pattern > </filter-mapping >
1 2 3 4 5 6 @Override public void init (FilterConfig filterConfig) throws ServletException { String encoding = filterConfig.getInitParameter("encoding" ); System.out.println("MyFilter01创建了..." +encoding); }
映射路径 假设有一个管理员权限的过滤器,它应该对用户发出的管理员功能的请求进行条件的过滤。但是当用户发出登录、注册等请求的时候,不应该进行过滤。所以我们过滤器,应该有选择的过滤器请求。这就需要学习配置过滤器不同的映射路径,从而让过滤器过滤希望过滤器的请求。
完全路径匹配 以”/“开始
1 /demo01 ---> 过滤器只能拦截路径/demo01;
目录匹配 以”/“开始 以 *结束 .
1 /* --->当前项目下的所有的路径都可以拦截; /aa/* ---> 可以拦截 /aa/bb, /aa/bb/cc
扩展名匹配 以”*”开始 例如: *.jsp *.do
1 *.do--->可以拦截路径的后缀是 do的 ; *.jsp--->拦截所有JSP
缺省匹配 / 除了jsp以外的资源都匹配
当前Filter里面不支持, Servlet里面可行的. 后面在Servlet里面遇到
小结
完全路径匹配
目录匹配
1 /* 匹配所有的资源 /aa/* 匹配以/aa开头的资源
扩展名匹配
1 *.jsp 匹配上了所有的jsp *.html 匹配上了所有的html
缺省匹配 / 匹配除了jsp以为的所有的资源
拦截方式 有了上面学习的映射路径,我们可以控制过滤器过滤指定的内容,但是我们在访问资源的时候,并不是每次都是之间访问,有时是以转发的方式访问的,这就需要我们要让过滤器可以区分不同的访问资源的方式,有不同的拦截方式。 是通过 DispatcherType 来指定的.
小结
通过dispatcherTypes配置拦截方式
拦截方式的这个值,我们可以配置多个
1 @WebFilter(value = {"/demo06"},dispatcherTypes={DispatcherType.FORWARD,DispatcherType.REQUEST})
一般情况下, 转发我们不会过滤的. 转发属于服务器内部的行为. 直接使用默认值的情况偏多
过滤器链(filterChain) 过滤器链作用:一个请求可能被多个过滤器所过滤,只有当所有过滤器都放行,请求才能到达目标资源,如果有某一个过滤器没有放行,那么请求则无法到达后续过滤器以及目标资源
小结
过滤器链执行顺序
配置文件: 谁先配置filter-mapping
谁先执行
注解方式: 按照Filter的首字母顺序 eg: AFilter BFilter A在B的前面, AFilter先执行
如果既有注解方式配置的过滤器,又有配置文件方式配置的过滤器,那么配置文件方式配置的过滤器先进行过滤
案例一: 统一全网站请求的中文乱码的处理 需求分析 在整个网站中,可能会有get请求或post请求向服务器提交参数.参数中往往有中文信息.在后台每个Servlet中都需要去处理乱码.
我们想做的是:请求到达Servlet中.就可以直接调用getParameter方法获得请求参数,请求参数已经没有乱码了.
思路分析
代码实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package com.nbchen.filter;import javax.servlet.*;import javax.servlet.annotation.WebFilter;import java.io.IOException;@WebFilter("/*") public class EncodingFilter implements Filter { @Override public void destroy () { } @Override public void doFilter (ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { req.setCharacterEncoding("UTF-8" ); chain.doFilter(req, resp); } @Override public void init (FilterConfig config) throws ServletException { } }
案例二: 非法字符过滤 需求分析 当用户发出非法言论的时候,提示用户言论非法。
效果:
第一个版本 思路分析
代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 package com.nbchen.filter;import javax.servlet.*;import javax.servlet.annotation.WebFilter;import java.io.IOException;@WebFilter("/*") public class IllegalCharFilter implements Filter { @Override public void destroy () { } @Override public void doFilter (ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { String content = req.getParameter("content" ); if (content != null ) { if (content.contains("你大爷" )) { resp.setContentType("text/html;charset=UTF-8" ); resp.getWriter().write("评论中包含非法字符,请重新评论" ); return ; } } chain.doFilter(req, resp); } @Override public void init (FilterConfig config) throws ServletException { } }
第二个版本 思路分析
代码实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 package com.nbchen.filter;import javax.servlet.*;import javax.servlet.annotation.WebFilter;import java.io.*;import java.util.ArrayList;import java.util.List;@WebFilter("/*") public class IllegalCharFilter implements Filter { private List<String> strList = new ArrayList <>(); @Override public void destroy () { } @Override public void doFilter (ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { String content = req.getParameter("content" ); if (content != null ) { for (String str : strList) { if (content.contains(str)) { resp.getWriter().write("评论中包含非法字符,请重新评论!!!" ); return ; } } } chain.doFilter(req, resp); } @Override public void init (FilterConfig config) throws ServletException { InputStream is = IllegalCharFilter.class.getClassLoader().getResourceAsStream("IllegalWords.txt" ); try { BufferedReader bfr = new BufferedReader (new InputStreamReader (is,"UTF-8" )); String str = null ; while (( str = bfr.readLine()) != null ) { strList.add(str); } } catch (Exception e) { e.printStackTrace(); }finally { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } }
第三个版本 思路分析
动态代理的作用是:
在不修改类的源码的基础上,增强类的方法
可以减少重复代码,在执行方法之前添加前置操作、执行方法之后添加后置操作(Spring的AOP思想)
我们可以在不写接口的实现类的情况下,创建接口的对象(mybatis框架)
代理模式的分类:
静态代理
动态代理
静态代理和动态代理的区别:
静态代理中必须存在代理类
静态代理的代理关系是在编译的时候确定的,而动态代理的代理关系是在程序运行的时候才确定的
代码实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 package com.nbchen.filter;import javax.servlet.*;import javax.servlet.annotation.WebFilter;import javax.servlet.http.HttpServletRequest;import java.io.*;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.util.ArrayList;import java.util.List;@WebFilter("/*") public class IllegalCharFilter implements Filter { private List<String> strList = new ArrayList <>(); @Override public void destroy () { } @Override public void doFilter (ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { HttpServletRequest request = (HttpServletRequest) req; ClassLoader classLoader = req.getClass().getClassLoader(); HttpServletRequest requestProxy = (HttpServletRequest) Proxy.newProxyInstance(classLoader, new Class []{HttpServletRequest.class}, new InvocationHandler () { @Override public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { if (method.getName().equals("getParameter" )) { String value = (String) method.invoke(request, args); for (String str : strList) { if (value.contains(str)) { String start = "" ; for (int i=0 ;i<str.length();i++){ start += "*" ; } value = value.replace(str,start); } } return value; } return method.invoke(request,args); } }); chain.doFilter(requestProxy, resp); } @Override public void init (FilterConfig config) throws ServletException { InputStream is = IllegalCharFilter.class.getClassLoader().getResourceAsStream("IllegalWords.txt" ); try { BufferedReader bfr = new BufferedReader (new InputStreamReader (is,"UTF-8" )); String str = null ; while (( str = bfr.readLine()) != null ) { strList.add(str); } } catch (Exception e) { e.printStackTrace(); }finally { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } }
Listener监听器 Listener概述 什么是Listener 监听器就是一个Java类,用来监听其他的JavaBean对象的变化
在javaweb中监听器就是监听三个域对象 的状态的。request,session,servletContext(application)
监听器的应用 主要在Swing编程
在Android/ios大量应用
JS里面的事件
小结
什么是监听器? 就是一个类(对象), 用来监听其它JavaBean状态的
我们学的是web里面的监听器, web里面的监听器是监听三个域对象的
监听器应用
在Android/ios大量应用
JS里面的事件
初始化工作(监听ServletContext)
javaweb中的监听器 javaweb的监听器 javaweb的监听器:监听ServletContext,HttpSession,ServletRequest三个域对象状态
事件源和监听器绑定的过程:通过配置web.xml完成
JavaWeb中的监听器类型
只讲解监听ServletContext的创建和销毁.
ServletContext是什么时候创建: 服务器启动的时候
ServletContext是什么时候销毁: 服务器关闭的时候
所以监听ServletContext创建和销毁,就能监听服务器的启动和关闭,就可以在服务器启动和关闭的时候执行一些代码(比如在服务器启动的时候读取Spring的配置文件,创建Spring的核心容器)
JavaWeb的监听器使用步骤
创建一个类实现监听器接口
在web.xml进行配置(绑定)
小结
学习监听器就学一个 监听ServletContext的创建和销毁
监听ServletContext的创建和销毁的监听器 说明: 监听ServletContext对象的创建和销毁
方法
问题
ServletContext对象何时创建和销毁:应用在,它就在
创建:服务器启动时候(前提是这个项目在这个服务器里面). 服务器为每个WEB应用创建一个单独的ServletContext.
销毁:服务器关闭的时候,或者项目从服务器中移除.
企业中应用
初始化工作.
加载配置文件:Spring框架,ContextLoaderListener
步骤:
创建一个类实现ServletContextListener
在web.xml配置
代码:
JAVA代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package com.nbchen.listener;import javax.servlet.ServletContextEvent;import javax.servlet.ServletContextListener;public class MyContextListener implements ServletContextListener { @Override public void contextInitialized (ServletContextEvent sce) { System.out.println("监听到了服务器的启动....创建spring的核心容器" ); } @Override public void contextDestroyed (ServletContextEvent sce) { System.out.println("监听到了服务器的关闭....销毁spring的核心容器" ); } }
配置(web.xml)
1 2 3 4 5 6 7 8 9 <?xml version="1.0" encoding="UTF-8" ?> <web-app xmlns ="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version ="3.1" > <listener > <listener-class > com.itheima.listener.MyContextListener</listener-class > </listener > </web-app >
小结
步骤
创建一个类实现ServletContextListener接口
在web.xml配置
应用: Spring框架里面, 服务器启动时候, 就加载Spring框架 初始化
邮箱(了解) 邮件流程
邮件服务器 服务器
硬件+软件(eg: mysql, tomcat…)
邮件服务器
租(企业邮箱)
自己搭建
邮件软件安装
服务端
客户端
邮件的发送
直接通过Foxmail发送
通过java代码发送
MailUtil
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 package com.nbchen.utils;import javax.mail.*;import javax.mail.internet.InternetAddress;import javax.mail.internet.MimeMessage;import javax.mail.internet.MimeMessage.RecipientType;import java.util.Properties;public class MailUtil { private MailUtil () {} public static void sendMail (String toEmail, String emailMsg) throws Exception { Properties props = new Properties (); props.put("mail.smtp.host" , "localhost" ); props.put("mail.smtp.auth" , "true" ); Authenticator auth = new Authenticator () { public PasswordAuthentication getPasswordAuthentication () { return new PasswordAuthentication ("service" , "123456" ); } }; Session session = Session.getInstance(props, auth); Message message = new MimeMessage (session); message.setFrom(new InternetAddress ("service@itheima104.com" )); message.setRecipient(RecipientType.TO, new InternetAddress (toEmail)); message.setSubject("用户激活" ); message.setContent(emailMsg, "text/html;charset=UTF-8" ); Transport.send(message); } }
Jsp JSP入门 JSP概述 什么是JSP Java server page(java服务器页面). JSP本质就是Servlet
它和servle技术一样,都是SUN公司定义的一种用于开发动态web资源的技术。
JSP=html(js,css)+java(servlet)+jsp(内置对象、指令、动作标签等等)特有的内容
JSP产生的原因 需求: 我们要向页面动态输出一个表格. 发现特别的繁琐
servlet在展示页面的时候,相当的繁琐。sun公司为了解决这个问题,参照asp开发了一套动态网页技术jsp。
JSP执行原理 JSP会翻译(通过默认的JspServlet,JSP引擎)成Servlet(.java),Servlet编译成class文件
JSP执行流程
第一次访问的xxx.jsp时候,服务器收到请求,JspServlet会去查找对应的jsp文件
找到之后,服务器会将这个jsp文件转换成java文件(Servlet)
服务器编译java文件,生成class文件
服务器运行class文件,生成动态的内容
服务器收到内容之后,返回给浏览器
小结
JSP: java 服务器 页面, sun公司定义的动态资源, 本质就是Servlet
JSP产生的原因: Servlet在动态展示很麻烦, jsp展示方便一点
JSP基本语法 JSP脚本 我们可以通过JSP脚本在JSP页面上编写Java代码. 一共有三种方式:
类型
翻译成Servlet对应的部分
注意
<%…%>:Java程序片段
翻译成Service()方法里面的内容, 局部的
<%=…%>:输出表达式
翻译成Service()方法里面的内容,相当于调用out.print()
输出表达式不能以;结尾
<%!…%>:声明成员变量(肯定不用)
翻译成Servlet类里面的内容
1 2 3 4 5 6 7 8 <% for(int i = 0; i < 10;i++){ out.print("i="+i); %> <hr/> <% } %>
JSP注释
注释类型
HTML注释
JAVA注释 //; /* */
JSP注释; <%–注释内容–%>
注释快捷键:Ctrl+Shift+/
小结
脚本
<%%>
翻译成了service()方法里面的局部内容
<%=%>
输出, 翻译成了service()方法里面的out.print()
<%!%>
翻译成了servlet类里面的全局内容
注释
登录案例的优化 优化登录失败的效果 LoginServlet的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 package com.nbchen.servlet;import com.nbchen.bean.User;import com.nbchen.utils.DruidUtil;import org.apache.commons.dbutils.QueryRunner;import org.apache.commons.dbutils.handlers.BeanHandler;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 javax.servlet.http.HttpSession;import java.io.IOException;import java.sql.SQLException;@WebServlet("/login") public class LoginServlet extends HttpServlet { @Override protected void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } @Override protected void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8" ); response.setContentType("text/html;charset=UTF-8" ); String username = request.getParameter("username" ); String password = request.getParameter("password" ); String checkCode = request.getParameter("checkCode" ); HttpSession session = request.getSession(); String code = (String) session.getAttribute("code" ); if (code.equalsIgnoreCase(checkCode)) { QueryRunner queryRunner = new QueryRunner (DruidUtil.getDataSource()); String sql = "select * from user where username=? and password=?" ; try { User user = queryRunner.query(sql, new BeanHandler <>(User.class), username, password); if (user != null ) { response.getWriter().write("登录成功" ); }else { String errorMsg = "用户名或密码错误" ; request.setAttribute("errorMsg" ,errorMsg); request.getRequestDispatcher("login.jsp" ).forward(request, response); } } catch (SQLException e) { e.printStackTrace(); String errorMsg = "登录失败" ; request.setAttribute("errorMsg" ,errorMsg); request.getRequestDispatcher("login.jsp" ).forward(request, response); } }else { String errorMsg = "验证码错误" ; request.setAttribute("errorMsg" ,errorMsg); request.getRequestDispatcher("login.jsp" ).forward(request, response); } } }
login.jsp的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 <%-- Created by IntelliJ IDEA. User: Fanyi Xiao Date: 2020 /8 /26 Time: 15 :15 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8" > <title>Title</title> </head> <body> <center> <% String errorMsg = (String) request.getAttribute("errorMsg" ); if (errorMsg == null ) { errorMsg = "" ; } %> <h1>用户登录</h1> <span style="color: red" ><%=errorMsg%></span> <form action="login" method="post" > 姓名:<input type="text" name="username" /><br/> 密码:<input type="password" name="password" /><br/> 验证码:<input type="text" name="checkCode" ><br> <img src="checkCode" id="code" ><a href="javascript:;" onclick="changeCheckCode()" >换一换</a><br> <input type="submit" value="登录" /> </form> <script> function changeCheckCode () { document.getElementById("code" ).setAttribute("src" ,"checkCode?date=" +new Date ()) } </script> </center> </body> </html>
优化登录成功的效果 LoginServlet的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 @WebServlet("/login") public class LoginServlet extends HttpServlet { @Override protected void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } @Override protected void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { request.setCharacterEncoding("UTF-8" ); response.setContentType("text/html;charset=UTF-8" ); String username = request.getParameter("username" ); String password = request.getParameter("password" ); String checkCode = request.getParameter("checkCode" ); HttpSession session = request.getSession(); String code = (String) session.getAttribute("code" ); if (code.equalsIgnoreCase(checkCode)) { QueryRunner queryRunner = new QueryRunner (DruidUtil.getDataSource()); String sql = "select * from user where username=? and password=?" ; User user = queryRunner.query(sql, new BeanHandler <>(User.class), username, password); if (user != null ) { session.setAttribute("user" ,user); response.sendRedirect("/userDemo/success.jsp" ); }else { request.setAttribute("msg" ,"用户名或密码错误" ); request.getRequestDispatcher("/login.jsp" ).forward(request, response); } }else { request.setAttribute("msg" ,"验证码错误" ); request.getRequestDispatcher("/login.jsp" ).forward(request, response); } } catch (Exception e) { e.printStackTrace(); request.setAttribute("msg" ,"服务器异常请稍后再试" ); request.getRequestDispatcher("/login.jsp" ).forward(request, response); } } }
success.jsp的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 <%@ page import ="com.itheima.pojo.User" %><%-- Created by IntelliJ IDEA. User: Fanyi Xiao Date: 2020 /7 /14 Time: 16 :02 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html lang="en" > <head> <meta charset="UTF-8" > <title>成功页面</title> </head> <body> <% User user = (User) session.getAttribute("user" ); String nickname = null ; if (user != null ) { nickname = user.getNickname(); } %> <h1>欢迎回来,<%=nickname%></h1> </body> </html>
案例-记住用户名案例 需求
分析
在LoginServlet里面, 如果用户登录成功:
//判断用户是否勾选了记住用户名
//勾选了, 把用户名存到Cookie
//未勾选则清除已经保存的用户名
在login.jsp页面 从cookie取出展示
实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 package com.nbchen.servlet;import com.nbchen.pojo.User;import com.nbchen.utils.DruidUtil;import org.apache.commons.dbutils.QueryRunner;import org.apache.commons.dbutils.handlers.BeanHandler;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.*;import java.io.IOException;@WebServlet("/login") public class LoginServlet extends HttpServlet { @Override protected void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } @Override protected void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8" ); response.setContentType("text/html;charset=UTF-8" ); String username = request.getParameter("username" ); String password = request.getParameter("password" ); String remember = request.getParameter("remember" ); Cookie cookie = new Cookie ("username" ,username); cookie.setPath(request.getContextPath()); if (remember != null ) { cookie.setMaxAge(7 *24 *60 *60 ); }else { cookie.setMaxAge(0 ); } response.addCookie(cookie); String checkCode = request.getParameter("checkCode" ); HttpSession session = request.getSession(); String code = (String) session.getAttribute("code" ); if (code.equalsIgnoreCase(checkCode)) { String sql = "select * from user where username=? and password=?" ; QueryRunner queryRunner = new QueryRunner (DruidUtil.getDataSource()); try { User user = queryRunner.query(sql, new BeanHandler <>(User.class), username, password); if (user != null ) { session.setAttribute("user" ,user); request.getRequestDispatcher("success.jsp" ).forward(request, response); }else { String errorMsg = "用户名或密码错误" ; request.setAttribute("errorMsg" ,errorMsg); request.getRequestDispatcher("login.jsp" ).forward(request, response); } } catch (Exception e) { e.printStackTrace(); String errorMsg = "用户名或密码错误" ; request.setAttribute("errorMsg" ,errorMsg); request.getRequestDispatcher("login.jsp" ).forward(request, response); } }else { String errorMsg = "验证码错误" ; request.setAttribute("errorMsg" ,errorMsg); request.getRequestDispatcher("login.jsp" ).forward(request, response); } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 <%-- Created by IntelliJ IDEA. User: Fanyi Xiao Date: 2020 /10 /15 Time: 14 :35 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>登录页面</title> </head> <body> <% String errorMsg = (String) request.getAttribute("errorMsg" ); if (errorMsg == null ) { errorMsg = "" ; } String username = "" ; Cookie[] cookies = request.getCookies(); if (cookies != null ) { for (Cookie cookie : cookies) { if (cookie.getName().equals("username" )) { username = cookie.getValue(); } } } %> <div style="color: red" ><%=errorMsg%></div> <form action="login" method="post" > <%--将变量username的值显示到用户名输入框--%> 用户名<input type="text" name="username" value="<%=username%>" ><br> 密码<input type="password" name="password" ><br> 验证码<input type="text" name="checkCode" ><br> <img id="checkCode" src="checkCode" /> <a href="javascript:;" onclick="changeCheckCode()" >换一换</a> <br> <input type="checkbox" name="remember" >记住用户名<br> <input type="submit" value="提交" > </form> <script> function changeCheckCode () { document.getElementById("checkCode" ).setAttribute("src" ,"checkCode?a=" +new Date ()) } </script> </body> </html>
小结
用户勾选了记住用户名,我们把用户名存到Cookie里面
在login.jsp里面 从cookie取出用户名展示
EL表达式 EL表达式概述 什么是El表达式 Expression Language: 表达式语言, jsp2.0之后内置在jsp里面
目的:为了使JSP写起来更加简单, 取值(取的域对象里面存的值)更加简单。(代替脚本 <% %>)
EL语法
EL表达式的用途 1.获取数据. 获取的是域(request,session,ServletContext)对象 中存储的数据
2.EL执行运算
使用EL表达式获取cookie中的数据
小结
EL表达式:表达式语言
语法:${el表达式}
作用:1. 获取域对象里面的数据 2. 执行运算
El获取数据 获取简单数据类型数据 语法:${requestScope|sessionScope|applicationScope.属性名}
;
快捷写法:${属性名}
, 属性名就是存在域对象里面的key
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 <%-- Created by IntelliJ IDEA. User: Fanyi Xiao Date: 2020 /7 /15 Time: 8 :52 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>使用EL表达式获取域对象中的简单类型的数据</title> </head> <body> <% application.setAttribute("msg" ,"applicationValue" ); %> 获取的application域对象中的msg=${applicationScope.msg}<br> 获取session域对象中的msg=${sessionScope.msg}<br> 获取request域对象中的msg=${requestScope.msg}<br> 获取存放在域对象中的msg=${msg} </body> </html>
获取数组 语法: ${key[下标]}
key就是域对象里面存的key
获取list 语法:${list属性名[index]}或者${list属性名.get(index)};list属性名就是存入域对象里面的key
获取Map 语法:${map属性名.键}或者${map属性名.get("键")},map属性名就是存入域对象里面的key
获取bean 语法:${key.javabean属性}
依赖getxxx()方法; eg: getPassword()—去掉get–>Password()—-首字母小写—>password
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 <%@ page import ="java.util.ArrayList" %> <%@ page import ="java.util.HashMap" %> <%@ page import ="java.util.List" %> <%@ page import ="java.util.Map" %> <%@ page import ="com.itheima.pojo.User" %><%-- Created by IntelliJ IDEA. User: Fanyi Xiao Date: 2020 /7 /15 Time: 9 :05 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>使用EL表达式获取存储在域对象中的复杂类型的数据</title> </head> <body> <% String[] arr = {"张三" ,"李四" ,"王五" ,"赵六" ,"田七" ,"狗娃" }; request.setAttribute("arr" , arr); List<String> list = new ArrayList <String>(); list.add("张三" ); list.add("李四" ); list.add("王五" ); list.add("赵六" ); list.add("田七" ); request.setAttribute("list" , list); Map<String,String> map = new HashMap <String,String>(); map.put("name" , "张三" ); map.put("password" , "123456" ); map.put("nickname" , "张三丰" ); request.setAttribute("map" , map); User user = new User (1 , "jay" , "台湾省" ); request.setAttribute("user" ,user); %> 获取存放在request域对象中的数组中的第三个元素:${arr[2 ]}<br> <%-- 在el表达式中,只要是根据下标获取元素,都可以写[index] --%> 获取存放在request域对象中的集合中的第四个元素:${list[3 ]}<br> <%-- 在el表达式中,只要是根据对应的属性的get方法去获取数据,都可以写成".属性名" 或者 ["属性名" ] --%> 获取存储在request域对象中的map中的nickname:${map.nickname}<br> 获取存放在request域对象中的user的address属性的值:${user.address} </body> </html>
小结 语法
获得简单类型的
获得数组类型的
获得List类型的
1 ${key.get(index)} 或者${key[index]}
获得Map类型的
1 ${key.get(键)} 或者${key.键} key是存到域对象里面的key
获得JavaBean类型的
EL执行运算
算数运算 +,-,*,/
+不能拼接字符串,如果+两端是字符串,那么会将字符串转换成数字之后再进行加法运算,如果+两端的字符串无法转换成数字,则会报错
逻辑运算 < >= <= != ==
关系运算 && || !
非空判断【重点】 empty,1. 判断一个对象是否为null, 2. 判断集合长度是否为0, 3. 判断一个字符串是否为空字符串
not empty
语法: ${empyt 属性名};属性名 就是域对象里面的key值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 <%@ page import ="java.util.ArrayList" %> <%@ page import ="java.util.List" %> <%@ page import ="com.itheima.pojo.User" %><%-- Created by IntelliJ IDEA. User: Fanyi Xiao Date: 2020 /7 /15 Time: 9 :35 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>empty 运算符的介绍</title> </head> <body> <% List<String> list = new ArrayList <String>(); list.add("张三丰" ); request.setAttribute("list" , list); request.setAttribute("msg" ,"requestValue" ); User user = new User (); request.setAttribute("u" ,user); %> 判断域对象中的list集合的长度是否为0 : ${empty list}<br> 判断域对象中的msg字符串是否为空字符串: ${empty msg}<br> 判断域对象中的user是否为null : ${empty u}<br> </body> </html>
小结
注意的地方: +只能做加法运算,不能拼接字符串
empty【重点】
语法
${empty key}
${not empty key}
作用
判断一个对象是否为null
判断一个集合长度是否为0
判断一个字符串是否是””
注意
如果是集合, 集合为null 是empty
如果是集合, 集合不为null 但是长度为0 还是empty
使用EL表达式获取存放在cookie中的数据(扩展) 语法:${cookie.cookie的name.value}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>使用el表达式获取存储在cookie中的数据</title> </head> <body> <%-- jsp里面是内置session对象,有了session对象,那么浏览器就会携带一个名为"JSESSIONID" 的cookie 我们的目标就是获取"JSESSIONID" 的值 --%> <% Cookie[] cookies = request.getCookies(); String cookieValue = null ; if (cookies != null ) { for (Cookie cookie : cookies) { if (cookie.getName().equals("JSESSIONID" )) { cookieValue = cookie.getValue(); } } } %> 使用原始方式获取的JSESSIONID的值为: <%=cookieValue%><br> <%-- ${cookie}表示获取这次请求中的所有cookie对象 ${cookie.JSESSIONID}表示获取名为"JSESSIONID" 的cookie对象 ${cookie.JSESSIONID.value}表示获取名为"JSESSIONID" 的cookie对象的value --%> 使用EL表达式获取JSESSIONID的值为: ${cookie.JSESSIONID.value} </body> </html>
JSTL标签库 JSTL标签库概述 什么是JSTL标签库 JSTL(JSP Standard Tag Library,JSP标准标签库)是一个不断完善的开放源代码的JSP标签库,是由apache的jakarta小组来维护的。这个JSTL标签库没有集成到JSP的, 要使用的话, 需要导jar包.
JSTL标签库的作用 为了简化在jsp页面上操作数据; eg: 遍历数据 判断数据等
JSTL标签库的类别
JSTL核心标签库 核心标签库使用步骤
导入jar包
在JSP页面上导入核心标签库<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
if标签
1 2 <c:if test="el表达式${..}"> </c:if>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 <%-- Created by IntelliJ IDEA. User: Fanyi Xiao Date: 2020 /8 /27 Time: 10 :10 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <html> <head> <title>使用jstl中的if 标签进行判断</title> </head> <body> <% request.setAttribute("age" ,17 ); %> <%-- if 标签有一个属性叫做test,它表示判断表达式,需要结合el一起使用 如果要表示相反的判断,则再添加一个if 标签,然后写相反的条件就行 if 标签还有一个属性叫做var ,表示将判断结果存储进域对象时候的key(了解) if 标签的第三个属性叫做scope,表示将判断结果存储进哪个域对象(了解) --%> <c:if test="${age >= 18}" var ="flag" scope="request" > 已成年 </c:if > <c:if test="${age < 18}" > 未成年 </c:if > <br> ${flag} </body> </html>
小结
1 <c:if test="${} "></c:if>
特点
如果test里面的是true, if标签体里面的就会执行
如果test里面的是false, if标签体里面的就不会执行
没有else的
choose标签
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 <%-- Created by IntelliJ IDEA. User: Fanyi Xiao Date: 2020 /7 /15 Time: 9 :59 To change this template use File | Settings | File Templates. --%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>jstl中的choose标签的使用介绍</title> </head> <body> <% request.setAttribute("course" ,"PHP" ); %> <c:choose> <%-- 一个when标签表示一个条件 --%> <c:when test="${course == 'Java'}" > 学习Java </c:when> <c:when test="${course == 'Android'}" > 学习Android </c:when> <c:when test="${course == 'C++'}" > 学习C++ </c:when> <c:otherwise> 学习,学个屁!!! </c:otherwise> </c:choose> </body> </html>
foreach标签
1 2 3 4 5 6 7 8 9 10 11 12 <%-- jstl中的forEach标签是用来代替for 循环语句 目标1 : 在浏览器上显示0 -9 的数字 begin属性: 从哪个下标开始遍历, 如果不写默认是从0 开始 end属性: 到哪个下标结束遍历,如果不写默认是遍历到集合/数组的最后一个元素 step属性: 表示遍历时候的步长,默认步长是1 var 属性: 表示将遍历的结果存放进域对象时候的key --%> <c:forEach begin="0" end="9" step="1" var ="i" > ${i} </c:forEach><br>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 <%@ page import ="java.util.ArrayList" %> <%@ page import ="java.util.List" %><%-- Created by IntelliJ IDEA. User: Fanyi Xiao Date: 2020 /7 /15 Time: 10 :15 To change this template use File | Settings | File Templates. --%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>forEach 标签的varStatus属性的介绍</title> </head> <body> <% List<String> list = new ArrayList <String>(); list.add("张三" ); list.add("李四" ); list.add("王五" ); list.add("赵六" ); list.add("田七" ); request.setAttribute("list" , list); %> <%-- forEach标签的varStatus属性:指定将遍历出来的每一个元素的状态存储进域对象时候的key 遍历出来的每一个元素都有一些状态(属性),比如: 下标 index: 计数 count: 当前元素的值 current: 是否是第一个元素: 是否是最后一个元素 --%> <table border="1" cellspacing="0" width="700" align="center" > <tr> <th>下标</th> <th>计数</th> <th>姓名</th> <th>是否是第一个元素</th> <th>是否是最后一个元素</th> </tr> <c:forEach items="${list}" varStatus="vst" > <tr> <td>${vst.index}</td> <td>${vst.count}</td> <td>${vst.current}</td> <td>${vst.first}</td> <td>${vst.last}</td> </tr> </c:forEach> </table> </body> </html>
小结
foreach标签
1 2 3 <c:foreach begin="从哪里开始" end="到哪里结束" var="每次遍历的赋值变量" step="步长"> //每遍历一次 foreach里面就执行一次 </c:foreach>
1 2 3 <c:foreach items="使用el从域对象里面取出集合" var="每次遍历的赋值变量" varStatus="遍历的状态"> //每遍历一次 foreach里面就执行一次 </c:foreach>
回顾el和JSTL的内容 el
el的语法: ${el表达式}
el的作用:
获取域对象中的数据,${key}
执行运算,el表达式中可以使用运算符:+-*/><与或非等等运算符都能使用,它还有一个特殊的运算符叫做empty
获取cookie中的数据: ${cookie.cookie的名字.value}
jstl
jstl的使用步骤:
导入jar包
在要使用jstl的jsp页面通过tablib指令引入核心标签库
使用jstl的核心标签库中的标签
jstl的常用标签:
if
choose
forEach
if标签
作用: 代替jsp页面中的if语句
属性:
test(必须的属性): 结合el表达式编写判断条件
var: 将判断结果存储进域对象时候的key
scope:将判断条件存储进哪个域对象
choose标签:
作用:代替多条件判断
它需要和when标签以及otherwise标签一起使用
forEach标签
作用: 代替jsp页面的for循环语句
属性:
begin 开始遍历的下标,如果没写,则从0开始遍历
end 结束遍历的下标,如果没写,则遍历到数组或者集合的最后一个元素
step 遍历的步长,如果没写就是1
var 遍历出来的每一个数据存储进域对象时候的key
varStatus 遍历出来的每一个数据的状态
index 下标
count 序号
first 是否是第一个元素
last 是否是最后一个元素
current 遍历出来的当前元素
综合案例和开发模式 案例-完成转账的案例v1 需求
当单击提交按钮,付款方向收款方安照输入的金额转账。
分析
实现 案例的准备工作
数据库的准备
1 2 3 4 5 6 7 8 9 10 11 create database day29; use day29; create table account( id int primary key auto_increment, name varchar(20), money double ); insert into account values (null,'jay',1000); insert into account values (null,'aobama',1000); insert into account values (null,'ww',1000);
页面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" > <title>Insert title here</title> </head> <body> <form method="post" action="" > <table border="1px" width="500px" align="center" > <tr> <td>付款方</td> <td><input type="text" name="from" ></td> </tr> <tr> <td>收款方</td> <td><input type="text" name="to" ></td> </tr> <tr> <td>金额</td> <td><input type="text" name="money" ></td> </tr> <tr> <td colspan="2" ><input type="submit" ></td> </tr> </table> </form> </body> </html>
jar包
工具类
配置文件
代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 package com.itheima.web.servlet;import com.itheima.utils.DruidUtil;import org.apache.commons.dbutils.QueryRunner;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 java.io.IOException;@WebServlet("/account") public class AccountServlet extends HttpServlet { @Override protected void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } @Override protected void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8" ); response.setContentType("text/html;charset=UTF-8" ); String fromName = request.getParameter("from" ); String toName = request.getParameter("to" ); Double money = Double.valueOf(request.getParameter("money" )); QueryRunner queryRunner = new QueryRunner (DruidUtil.getDataSource()); String sql1 = "update account set money=money-? where name=?" ; try { queryRunner.update(sql1,money,fromName); String sql2 = "update account set money=money+? where name=?" ; queryRunner.update(sql2,money,toName); response.getWriter().write("转账成功!!!" ); } catch (Exception e) { e.printStackTrace(); response.getWriter().write("转账失败!!!" ); } } }
小结
转账: 一个用户的钱减少, 一个用户的钱增加
开发模式
JSP的开发模式一【了解】 javaBean:实体类。特点:私有化的属性、公共的getter setter方法、无参的构造。
JSP的开发模式二 JSP + Servlet + JavaBean 称为MVC的开发模式.
MVC:开发模式
M:model 模型 (javaBean:封装数据)
V:View 视图 (JSP:展示数据)
C:controller 控制器 (Servlet:处理逻辑代码,做为控制器)
模式三: 三层架构
分层
包名(公司域名倒写)
表现层(web层)
com.itheima.web
业务层(service层)
com.itheima.service
持久层(数据库访问层)
com.itheima.dao
JavaBean
com.itheima.bean
工具类
com.itheima.utils
分层的意义:
解耦:降低层与层之间的耦合性。 (以后面向接口编程)
可维护性:提高软件的可维护性,对现有的功能进行修改和更新时不会影响原有的功能。
可扩展性:提升软件的可扩展性,添加新的功能的时候不会影响到现有的功能。
可重用性:不同层之间进行功能调用时,相同的功能可以重复使用。
小结
模式一: JSP+JavaBean【了解】
模式二: MVC
M model JavaBean
V View JSP
C Controller Servlet
三层架构
WEB层
业务层
持久层
三层架构中的包名:
表现层: web
业务层: service
数据访问层/持久层: dao
案例-完成转账的案例v2 使用三层架构改写转账案例
分析
实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 package com.itheima.web.servlet;import com.itheima.service.AccountService;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 java.io.IOException;@WebServlet("/account") public class AccountServlet extends HttpServlet { @Override protected void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } @Override protected void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8" ); response.setContentType("text/html;charset=UTF-8" ); String fromName = request.getParameter("from" ); String toName = request.getParameter("to" ); Double money = Double.valueOf(request.getParameter("money" )); try { AccountService accountService = new AccountService (); accountService.transfer(fromName,toName,money); response.getWriter().write("转账成功!!!" ); } catch (Exception e) { e.printStackTrace(); response.getWriter().write("转账失败!!!" ); } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package com.itheima.service;import com.itheima.dao.AccountDao;import java.sql.SQLException;public class AccountService { private AccountDao accountDao = new AccountDao (); public void transfer (String fromName,String toName,Double money) throws SQLException { accountDao.updateAccount(fromName,-money); accountDao.updateAccount(toName,money); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package com.itheima.dao;import com.itheima.utils.DruidUtil;import org.apache.commons.dbutils.QueryRunner;import java.sql.SQLException;public class AccountDao { private QueryRunner queryRunner = new QueryRunner (DruidUtil.getDataSource()); public void updateAccount (String name,Double money) throws SQLException { String sql = "update account set money=money+? where name=?" ; queryRunner.update(sql,money,name); } }
小结
WEB层 com.itheima.web
业务层 com.itheima.service xxService
持久层 com.itheima.dao xxDao
案例-完成转账的案例v3 需求:当单击提交按钮,付款方向收款方安照输入的金额转账。 使用手动事务进行控制
DBUtils实现事务管理
API
说明
QueryRunner()
创建QueryRunner对象. 手动提交事务时使用
query(connection,String sql, Object[] params, ResultSetHandler<T> rsh)
查询(需要传入Connection)
update(connection,String sql, Object… params)
更新
思路
实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 package com.itheima.service;import com.itheima.dao.AccountDao;import com.itheima.utils.DruidUtil;import java.sql.Connection;import java.sql.SQLException;public class AccountService { private AccountDao accountDao = new AccountDao (); public void transfer (String fromName,String toName,Double money) { Connection conn = null ; try { conn = DruidUtil.getDataSource().getConnection(); conn.setAutoCommit(false ); accountDao.updateAccount(conn,fromName, -money); int num = 10 / 0 ; accountDao.updateAccount(conn,toName, money); conn.commit(); } catch (Exception e) { e.printStackTrace(); try { conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } throw new RuntimeException ("转账失败" ); } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package com.nbchen.dao;import com.nbchen.utils.DruidUtil;import org.apache.commons.dbutils.QueryRunner;import java.sql.Connection;import java.sql.SQLException;public class AccountDao { private QueryRunner queryRunner = new QueryRunner (DruidUtil.getDataSource()); public void updateAccount (Connection connection,String name, Double money) throws SQLException { String sql = "update account set money=money+? where name=?" ; queryRunner.update(connection,sql,money,name); } }
小结
思想1: service层将异常try起来了,怎么才能让servlet还能够获取异常呢?
思想2: 如果在service和dao共享一个Connection对象
1 通过调用方法的时候将connection作为参数传递给Dao
技术点1 : 怎么开启、提交、回滚事务
1 2 3 4 connection.setAutoCommit(false)开启事务 connection.commit()提交事务 connection.rollback()回滚事务 注意: 开启事务的连接和执行SQL语句的连接要是同一个
技术点2 : 在使用DBUtils执行SQL语句的时候,怎么才能指定使用哪个连接呢?
1 调用queryRunner对象的update或者query方法的时候,可以传入connection
案例-完成转账的案例v4
ThreadLocal 在“事务传递参数版”中,我们必须修改方法的参数个数,传递链接,才可以完成整个事务操作。如果不传递参数,是否可以完成?在JDK中给我们提供了一个工具类:ThreadLocal,此类可以在一个线程中共享数据。
java.lang.ThreadLocal,该类提供了线程局部 (thread-local) 变量,用于在当前线程中共享数据。ThreadLocal工具类底层就是一个Map,key存放的当前线程,value存放需要共享的数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 package com.itheima;public class TestMain { public static void main (String[] args) { ThreadLocal<String> threadLocal = new ThreadLocal <>(); threadLocal.set("jay" ); threadLocal.set("aobama" ); new Thread (new Runnable () { @Override public void run () { threadLocal.set("jay" ); String s = threadLocal.get(); System.out.println("在新线程中获取ThreadLocal中的值为:" + s); } }).start(); String str = threadLocal.get(); System.out.println("在主线程中获取ThreadLocal对象中的值:" + str); } }
思路
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 package com.itheima.utils;import com.alibaba.druid.pool.DruidDataSourceFactory;import javax.sql.DataSource;import java.io.InputStream;import java.sql.Connection;import java.sql.SQLException;import java.util.Properties;public class DruidUtil { private static ThreadLocal<Connection> threadLocal = new ThreadLocal <>(); private static DataSource dataSource; static { try { Properties properties = new Properties (); InputStream is = DruidUtil.class.getClassLoader().getResourceAsStream("druid.properties" ); properties.load(is); dataSource = DruidDataSourceFactory.createDataSource(properties); } catch (Exception e) { e.printStackTrace(); } } public static DataSource getDataSource () { return dataSource; } public static Connection getConnection () throws SQLException { Connection connection = threadLocal.get(); if (connection == null ) { connection = dataSource.getConnection(); threadLocal.set(connection); } return connection; } public static void clear () throws SQLException { threadLocal.get().setAutoCommit(true ); threadLocal.get().close(); threadLocal.remove(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 package com.itheima.service;import com.itheima.dao.AccountDao;import com.itheima.utils.DruidUtil;import java.sql.Connection;import java.sql.SQLException;public class AccountService { private AccountDao accountDao = new AccountDao (); public void transfer (String fromName,String toName, Double money) { Connection conn = null ; try { conn = DruidUtil.getConnection(); conn.setAutoCommit(false ); accountDao.updateAccount(fromName, -money); int num = 10 / 0 ; accountDao.updateAccount(toName, money); conn.commit(); } catch (Exception e) { e.printStackTrace(); try { conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } throw new RuntimeException (e.getMessage()); }finally { try { DruidUtil.clear(); } catch (SQLException e) { e.printStackTrace(); } } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package com.itheima.dao;import com.itheima.utils.DruidUtil;import org.apache.commons.dbutils.QueryRunner;import java.sql.SQLException;public class AccountDao { private QueryRunner queryRunner = new QueryRunner (DruidUtil.getDataSource()); public void updateAccount (String name, Double money) throws SQLException { String sql = "update account set money=money+? where name=?" ; queryRunner.update(DruidUtil.getConnection(),sql,money,name); } }
小结
TheadLocal: jdk提供的一个对象. 只要是在同一个线程里面, 是可以共用的.
抽取了DruidUtil的getConnectionFromThreadLocal()方法, service和Dao里面的Connection都是从getConnectionFromThreadLocal()方法获取
补充案例: 显示所有用户 在list.jsp页面中显示user表中的所有用户的信息
拷贝jar、配置文件、工具类
创建index.jsp和list.jsp
创建包结构、pojo类
创建ShowAllServlet、UserService、UserDao
index.jsp代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <%-- Created by IntelliJ IDEA. User: Fanyi Xiao Date: 2020 /7 /15 Time: 12 :08 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>首页</title> </head> <body> <a href="/showAll" >查看所有用户信息</a> </body> </html>
ShowAllServlet代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 package com.itheima.web.servlet;import com.itheima.pojo.User;import com.itheima.service.UserService;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 java.io.IOException;import java.util.List;@WebServlet("/showAll") public class ShowAllServlet extends HttpServlet { @Override protected void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } @Override protected void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { UserService userService = new UserService (); List<User> userList = userService.findAll(); request.setAttribute("list" ,userList); request.getRequestDispatcher("/list.jsp" ).forward(request, response); } catch (Exception e) { e.printStackTrace(); } } }
UserService代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package com.itheima.service;import com.itheima.dao.UserDao;import com.itheima.pojo.User;import java.sql.SQLException;import java.util.List;public class UserService { private UserDao userDao = new UserDao (); public List<User> findAll () throws SQLException { List<User> userList = userDao.findAll(); return userList; } }
UserDao代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package com.itheima.dao;import com.itheima.pojo.User;import com.itheima.utils.DruidUtil;import org.apache.commons.dbutils.QueryRunner;import org.apache.commons.dbutils.handlers.BeanListHandler;import java.sql.SQLException;import java.util.List;public class UserDao { private QueryRunner queryRunner = new QueryRunner (DruidUtil.getDataSource()); public List<User> findAll () throws SQLException { String sql = "select * from user" ; List<User> userList = queryRunner.query(sql, new BeanListHandler <>(User.class)); return userList; } }
list.jsp代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 <%-- Created by IntelliJ IDEA. User: Fanyi Xiao Date: 2020 /7 /15 Time: 12 :08 To change this template use File | Settings | File Templates. --%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>展示页面</title> </head> <body> <table border="1" cellspacing="0" width="800px" align="center" > <tr> <th>序号</th> <th>用户名</th> <th>密码</th> <th>地址</th> <th>昵称</th> <th>性别</th> <th>邮箱</th> </tr> <%-- 遍历出域对象里面的list中的每一个user --%> <c:forEach items="${list}" var ="user" varStatus="vst" > <tr> <td>${vst.count}</td> <td>${user.username}</td> <td>${user.password}</td> <td>${user.address}</td> <td>${user.nickname}</td> <td>${user.gender}</td> <td>${user.email}</td> </tr> </c:forEach> </table> </body> </html>
注册登录案例改成三层架构 RegisterServlet代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 package com.itheima.web.servlet;import com.itheima.bean.User;import com.itheima.service.UserService;import org.apache.commons.beanutils.BeanUtils;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 java.io.IOException;import java.util.Map;@WebServlet("/register") public class RegisterServlet extends HttpServlet { private UserService userService = new UserService (); @Override protected void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } @Override protected void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8" ); response.setContentType("text/html;charset=utf-8" ); Map<String, String[]> map = request.getParameterMap(); User user = new User (); try { BeanUtils.populate(user,map); user.setStatus("0" ); userService.register(user); response.sendRedirect("login.html" ); } catch (Exception e) { e.printStackTrace(); response.getWriter().write("注册失败" ); } } }
LoginServlet代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 package com.itheima.web.servlet;import com.itheima.bean.User;import com.itheima.service.UserService;import com.itheima.utils.CookieUtil;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.*;import java.io.IOException;@WebServlet("/login") public class LoginServlet extends HttpServlet { private UserService userService = new UserService (); @Override protected void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } @Override protected void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8" ); response.setContentType("text/html;charset=UTF-8" ); String username = request.getParameter("username" ); String password = request.getParameter("password" ); String checkCode = request.getParameter("checkCode" ); HttpSession session = request.getSession(); String code = (String) session.getAttribute("code" ); String remember = request.getParameter("remember" ); if (code.equalsIgnoreCase(checkCode)) { try { User user = userService.login(username, password); if (user != null ) { if (remember != null ) { Cookie cookie = CookieUtil.createAndSetCookie("username" , username, 7 * 24 * 60 * 60 , request.getContextPath()); response.addCookie(cookie); }else { Cookie cookie = CookieUtil.createAndSetCookie("username" , username, 0 , request.getContextPath()); response.addCookie(cookie); } session.setAttribute("user" ,user); response.sendRedirect("success.jsp" ); }else { String errorMsg = "用户名或密码错误" ; request.setAttribute("errorMsg" ,errorMsg); request.getRequestDispatcher("login.jsp" ).forward(request, response); } } catch (Exception e) { e.printStackTrace(); String errorMsg = "登录失败" ; request.setAttribute("errorMsg" ,errorMsg); request.getRequestDispatcher("login.jsp" ).forward(request, response); } }else { String errorMsg = "验证码错误" ; request.setAttribute("errorMsg" ,errorMsg); request.getRequestDispatcher("login.jsp" ).forward(request, response); } } }
UserService代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package com.itheima.service;import com.itheima.bean.User;import com.itheima.dao.UserDao;public class UserService { private UserDao userDao = new UserDao (); public void register (User user) throws Exception { userDao.saveUser(user); } public User login (String username,String password) throws Exception { return userDao.findUser(username,password); } }
UserDao代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 package com.itheima.dao;import com.itheima.bean.User;import com.itheima.utils.DruidUtil;import org.apache.commons.dbutils.QueryRunner;import org.apache.commons.dbutils.handlers.BeanHandler;import java.sql.SQLException;public class UserDao { public void saveUser (User user) throws Exception { QueryRunner queryRunner = new QueryRunner (DruidUtil.getDataSource()); String sql = "insert into user values (null,?,?,?,?,?,?,?)" ; queryRunner.update(sql,user.getUsername(),user.getPassword(),user.getAddress(),user.getNickname(),user.getGender(),user.getEmail(),user.getStatus()); } public User findUser (String username,String password) throws Exception { QueryRunner queryRunner = new QueryRunner (DruidUtil.getDataSource()); String sql = "select * from user where username=? and password=?" ; User user = queryRunner.query(sql, new BeanHandler <>(User.class), username, password); return user; } }