0%

事务集应该作为一个整体进行操作,而不是单个语句的执行,一个逻辑操作单元。

要么所有的事物都被提交,否则就发生事务回滚

那些操作发生数据提交:

  1. 增删改一旦执行,就会提交
  2. 关闭链接,也会自动
1
2
3
4
5
6
第一步:事务是自动提交的我们 手动关闭自动提交
conn.setAutoCommit(false);
第二步:如果没有异常就提交事务
conn.commit();
第三步:如果抛出异常 我们我们就让他回滚
conn.rollback();

在以下这个表格中,AA与BB进行相互转账,为保证操作的可靠性,我们需要通过事务保障操作的可行性

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
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Properties;

/**
* @author zss
*/
public class TransferAccount {
public static void main(String[] args) throws IOException, ClassNotFoundException, SQLException {
Properties properties=new Properties();
properties.load(new FileReader("src\\Information.properties"));
String jdbc= properties.getProperty("jdbcJar");
String url= properties.getProperty("url");
String password= properties.getProperty("password");
String user= properties.getProperty("user");
Class.forName(jdbc);
Connection connection= DriverManager.getConnection(url,user,password);
String sql1="update user_table set balance=balance-100 where user=(?) ";
String sql2="update user_table set balance=balance+100 where user=(?) ";
PreparedStatement preparedStatement1= connection.prepareStatement(sql1);
PreparedStatement preparedStatement2= connection.prepareStatement(sql2);
preparedStatement1.setString(1,"AA");
preparedStatement2.setString(1,"BB");
//AA转账
preparedStatement1.executeUpdate();
//发生故障
System.out.println(10/0);
//BB接受账单
preparedStatement2.executeUpdate();
preparedStatement1.close();
preparedStatement2.close();
connection.close();
}
}

image-20220330160902140

我们可以发现一笔钱无缘无故的消失了,但是当我们使用事务进行处理时

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
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Properties;

/**
* @author zss
*/
public class TransferAccount {
public static void main(String[] args) throws IOException, ClassNotFoundException, SQLException {
Properties properties=new Properties();
properties.load(new FileReader("src\\Information.properties"));
String jdbc= properties.getProperty("jdbcJar");
String url= properties.getProperty("url");
String password= properties.getProperty("password");
String user= properties.getProperty("user");
Class.forName(jdbc);
Connection connection= DriverManager.getConnection(url,user,password);
connection.setAutoCommit(false);
String sql1="update user_table set balance=balance-100 where user=(?) ";
String sql2="update user_table set balance=balance+100 where user=(?) ";
PreparedStatement preparedStatement1= connection.prepareStatement(sql1);
PreparedStatement preparedStatement2= connection.prepareStatement(sql2);
preparedStatement1.setString(1,"AA");
preparedStatement2.setString(1,"BB");
try {


//AA转账
preparedStatement1.executeUpdate();
//发生故障
//System.out.println(10/0);
//BB接受账单
preparedStatement2.executeUpdate();}catch (ArithmeticException arithmeticException){
System.out.println("交易发生异常,事务进行回滚");
connection.rollback();
}
//如果没有发生异常则提交数据
connection.commit();
preparedStatement1.close();
preparedStatement2.close();
connection.close();
}
}

image-20220330161443587

我们可以看出数据交易正常进行

事务的属性

ACID:

  1. 原子性:事务不可分割,要么都发生,要么都不发生。
  2. 一致性:必须使数据库从一个一致性状态变换到另外一个一致性状态,比如金钱的总数
  3. 隔离性:事务不可以被其他事务干扰,内部操作必须是不被其他事务影响
  4. 持久性:数据一旦提交,数据中的改变是永久性的,其他操作均无法对其产生影响

数据库的并发问题

  1. 脏读:1与2两个事务,1读取了2已经更改但是还没有提交的字段,但是随后2发生了回滚,1读取的内容就变的无根据
  2. 不可重复读:1读取一个字段,2对其进行了更改,后来1再次重新读取时,值不相同
  3. 幻读:1对表中的全部行进行了同一操作,2又插入了一行,但是新插入的行并没有更改,和幻觉一样,也包含两次读取表格的数据不一致

JDBC链接步骤

1
2
3
url=jdbc:mysql://localhost:3306/userinformation
user=root
passWord=200101

注册驱动

驱动:即我们从官网下载的jar包,没有jar包,我们可以编译程序,但是程序无法运行,将jar包添加到依赖。

image-20220322143653633

注册驱动的两种方式

  • 多层注册,为了保证代码的整洁,这里将抛出异常的语句删除(com.mysql.jdbc.Driver()来源于我们刚刚导入的jar包,并不是idea自带的
1
2
Driver driver=new Driver();
DriverManager.registerDriver(driver);
  • 利用反射机制注册(最常用)
1
Class.forName("com.mysql.jdbc.Driver");

获取链接与获取数据库操作对象

1
2
3
4
//获取链接
Connection connection=DriverManager.getConnection(url,user,password);
//获取数据库操作对象
Statement statement=connection.createStatement();
1
2
3
4
5
6
7
8
9
//获取链接
Properties properties=new Properties();
properties.load(new FileReader("src/Mysql.properties"));
String url= properties.getProperty("url");
String user= properties.getProperty("user");
String passWord= properties.getProperty("passWord");
Connection connection=DriverManager.getConnection(url,user,passWord);
//获得数据库的操作对象
Statement statement=connection.createStatement();

定义sql语句以及结果判断

1
2
3
4
5
//定义操作语句
String insertSql="insert into usersimple values ('张山峰',12,'高管')";
String insertSql1="insert into usersimple values ('丽丽',25,'职员')";
String updateSql="update usersimple set 职位='职员' where 职位='高管' ";
String deleteSql="delete from usersimple where 用户名称='丽丽' ";

结果判断statement.executeUpdate(插入,更新,删除)

1
2
3
4
5
6
7
8
9
10
//结果判断
int count=0;
count=statement.executeUpdate(insertSql);
System.out.println(count==1?"插入成功":"插入失败");
count=statement.executeUpdate(insertSql1);
System.out.println(count==1?"插入成功":"插入失败");
count=statement.executeUpdate(updateSql);
System.out.println(count==1?"更新成功":"更新失败");
count=statement.executeUpdate(deleteSql);
System.out.println(count==1?"删除成功":"删除失败");

image-20220322151646099

image-20220322151658855

结果判断statement.executeQuery

在Java中,有一个ResultSet的数据类型,专门用于存储select的语句的结果,而且还拥有一个resultSet.next()的类似c语言中指针类型的操作。

next的初始位置在标题位置,next指向下一行,如果有则返回true否则为fasle

1
2
3
4
5
6
7
String selectSql="select * from usersimple";
ResultSet resultSet=statement.executeQuery(selectSql);
while (resultSet.next()){
String userName=resultSet.getString("用户名称");
int age=resultSet.getInt("年龄");
String position=resultSet.getString("职位");
System.out.println(userName+age+position);

关闭通道

  • 当我们执行jdbc时,这个时候是多个线程在同时运行,如果我们结束时候没有结束这些线程,他们会一直保存着,这个连接是与数据库服务器的一个连接,虽然你的方法结束了,但是这个资源依然存在数据库连接并没有释放,如果不设置close,长时间使用,你会发现自己的电脑卡的飞起。👻
  • 线程结束的顺序(就像栈那样,先进行链接,在获取对象,最后看结果,而关闭时,先进后出):

打开时:Connection -> PreparedStatement -> ResultSet
关闭时:ResultSet-> PreparedStatement -> Connection

  • 代码演示(要在finally语句中进行,保证结束后不会在进行其他操作)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
finally {
if (resultSet!=null){try{
resultSet.close();

}
catch (SQLException e) {
e.printStackTrace();}}
try {
if (statement!=null) {
statement.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try{
if (connection!=null){
connection.close();
}
}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
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.zss.jdbc;

import com.mysql.jdbc.Driver;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.sql.*;
import java.util.Properties;

/**
* @author zss
*/
public class JdbcLearn {
public static void main(String[] args) throws ClassNotFoundException, IOException, SQLException {
/*Driver driver=new Driver();
DriverManager.registerDriver(driver);*/
Class cla=Class.forName("com.mysql.jdbc.Driver");
//获取链接
Properties properties=new Properties();
properties.load(new FileReader("src/Mysql.properties"));
String url= properties.getProperty("url");
String user= properties.getProperty("user");
String passWord= properties.getProperty("passWord");
Connection connection=DriverManager.getConnection(url,user,passWord);
//获得数据库的操作对象
Statement statement=connection.createStatement();
//定义操作语句
String insertSql="insert into usersimple values ('张山峰',12,'高管')";
String insertSql1="insert into usersimple values ('丽丽',25,'职员')";
String updateSql="update usersimple set 职位='职员' where 职位='高管' ";
String deleteSql="delete from usersimple where 用户名称='丽丽' ";
//结果判断
int count=0;
count=statement.executeUpdate(insertSql);
System.out.println(count==1?"插入成功":"插入失败");
count=statement.executeUpdate(insertSql1);
System.out.println(count==1?"插入成功":"插入失败");
count=statement.executeUpdate(updateSql);
System.out.println(count==1?"更新成功":"更新失败");
count=statement.executeUpdate(deleteSql);
System.out.println(count==1?"删除成功":"删除失败");

String selectSql="select * from usersimple";
ResultSet resultSet=statement.executeQuery(selectSql);
while (resultSet.next()){
String userName=resultSet.getString("用户名称");
int age=resultSet.getInt("年龄");
String position=resultSet.getString("职位");
System.out.println(userName+age+position);
}
resultSet.close();
statement.close();
connection.close();
}
}

Statement的缺陷

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.zss.jdbc;

import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
/**
* @author 张硕硕
*/
public class test {
public static void main(String [] args){
Map<String,String> haha= test.initUi();
System.out.println(test.login(haha)?"登陆成功":"登陆失败");
}
private static Map<String ,String > initUi(){
Scanner scanner=new Scanner(System.in);
System.out.println("请输入您的用户名");
String userName=scanner.nextLine();
System.out.println("请输入您的登录密码");
String password=scanner.nextLine();
Map<String,String> userInformation=new HashMap<>();
userInformation.put("username",userName);
userInformation.put("password",password);
return userInformation;
}
private static boolean login(Map<String,String> userInformation){
boolean loginY=false;
Connection connection=null;
Statement statement=null;
ResultSet resultset=null;
try{
Class.forName("com.mysql.cj.jdbc.Driver");
connection= DriverManager.getConnection("jdbc:mysql://localhost:3306/userinformation","root","200101");
statement=connection.createStatement();
String sql="select * from user where 用户名= '"+userInformation.get("username")+"'and 密码= '"+userInformation.get("password")+"'";
resultset=statement.executeQuery(sql);
if (resultset.next()){loginY=true;}
}
catch (Exception e){e.printStackTrace();}

finally {
if (resultset!=null){try{resultset.close();}catch (Exception e){e.printStackTrace();}}
if (statement!=null){try{statement.close();}catch (Exception e){e.printStackTrace();}}
if (connection!=null){try{connection.close();}catch(Exception e){e.printStackTrace();}}

}
return loginY;
}

}

image-20220322161854943

image-20220322162721137

  • 大家按照如图中信息去输入,可以惊奇的发现,账号密码完全不对,可是怎么就可以登录???

是因为在编译过程中,数据将密码传输给mysql语句,导致mysql语句句意发生改变,大家仔细查看在调试过程中,sql语句句意发生了改变,导致这个验证根本无效,可以直接通过这个登录。

image-20220322162842524

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
package com.zss.jdbc;

import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
/**
* @author 张硕硕
*/
public class test {
public static void main(String [] args){
Map<String,String> haha= test.initUi();
System.out.println(test.login(haha)?"登陆成功":"登陆失败");
}
private static Map<String ,String > initUi(){
Scanner scanner=new Scanner(System.in);
System.out.println("请输入您的用户名");
String userName=scanner.nextLine();
System.out.println("请输入您的登录密码");
String password=scanner.nextLine();
Map<String,String> userInformation=new HashMap<>();
userInformation.put("username",userName);
userInformation.put("password",password);
return userInformation;
}
private static boolean login(Map<String,String> userInformation){
boolean loginY=false;
Connection connection=null;
PreparedStatement preparedStatement=null;
ResultSet resultset=null;
try{
Class.forName("com.mysql.cj.jdbc.Driver");
connection= DriverManager.getConnection("jdbc:mysql://localhost:3306/userinformation","root","200101");
String sql="select * from user where 用户名= ? and 密码= ? ";
preparedStatement=connection.prepareStatement(sql);
preparedStatement.setString(1,userInformation.get("username"));
preparedStatement.setString(2,userInformation.get("password"));
resultset= preparedStatement.executeQuery();
if (resultset.next()){loginY=true;}
}
catch (Exception e){e.printStackTrace();}

finally {
if (resultset!=null){try{resultset.close();}catch (Exception e){e.printStackTrace();}}
if (preparedStatement!=null){try{preparedStatement.close();}catch (Exception e){e.printStackTrace();}}
if (connection!=null){try{connection.close();}catch(Exception e){e.printStackTrace();}}

}
return loginY;
}

}

image-20220322163801146

说明

  1. 在大部分情况下,PreparedStatementStatement更加广泛,而且更加安全。
  2. PreparedStatementStatement运行更加快速,在mysql中,如果连续两条语句一模一样,这样编译器不会对语句进行重新编译,而是直接将赋值进行操作。
  3. 在部分特殊情况下,还是需要使用Statement,因为部分企业需求需要进行注入。(比如我们常见的商品按照价格排序界面,需要有order by desc/asc时,利用 PreparedStatement传输desc值或者asc值时,会为这两个值带上引号,语句无法识别,此时使用Statement更加方便。

资料来自代码随想录 (programmercarl.com)

基础知识

链表由数据域和指针域两部分组成,最后一个节点的指针域指向null

  • 单链表:只能单向查询
  • 双链表:每一个节点有两个指针域,一个指向下一个节点,一个指向上一个节点,可以双向查询
  • 循环链表:形成一个环的链表

链表的定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class ListNode{
//节点的值
int val;
//下一个节点
ListNode next;
//无参构造器
public ListNode(){

}
//节点的构造函数
public ListNode(int val){
this.val=val;
}
public ListNode(int val,ListNode next){
this.val=val;
this.next=next;
}
}

移除链表的元素

203. 移除链表元素 - 力扣(LeetCode) (leetcode-cn.com)

普通方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public ListNode removeElements(ListNode head, int val) {

while ( head!=null&&head.val==val ){
head=head.next;
}
if(head==null){
return head;
}
ListNode preNode=head;
ListNode curNode=head.next;
while (curNode != null) {
if (curNode.val==val){
preNode.next=curNode.next;
}else {
preNode=curNode;
}
curNode=curNode.next;
}

return head;
}

特殊方法

添加虚拟节点的方式,使得头节点也加入常规之中,规避了头节点为空的复杂情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public ListNode removeElements(ListNode head, int val) {
if(head==null){
return head;
}

ListNode dummy=new ListNode(-1,head);
ListNode preNode=dummy;
ListNode curNode=head;
while (curNode != null) {
if (curNode.val==val){
preNode.next=curNode.next;
}else {
preNode=curNode;
}
curNode=curNode.next;
}
//在这里返回dummy.next;是因为上面的方法中头节点在一直改变,head所代表的节点在一直发生改变,在这里可能原来的head.val==val,head已经消失了
return dummy.next;
}

其他题目

https://moonshuo.cn/posts/54869.html

链表相交

面试题 02.07. 链表相交 - 力扣(LeetCode)

普通方法

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 ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode tempA = headA;

//用于存储相交的节点
while (tempA != null) {
ListNode tempB = headB;
while (tempB != null) {
if (tempA == tempB) {
return tempA;
} else {
if (tempB.next != null) {
tempB = tempB.next;
} else {
break;
}
}
}
if (tempA.next != null) {
tempA = tempA.next;
} else {
return null;
}
}
return null;
}

哈希表方法

我们这里可以使用哈希表的特性,遍历两个节点,将这些值存入到哈希表中

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 ListNode getIntersectionNode(ListNode headA, ListNode headB) {
HashSet<ListNode> hashSet = new HashSet<>();
ListNode tempA = headA;
while (tempA != null) {
hashSet.add(tempA);
if (tempA.next != null) {
tempA = tempA.next;
} else {
break;
}
}
ListNode tempB = headB;
while (tempB != null) {
if (!hashSet.add(tempB)) {
return tempB;
} else {
if (tempB.next != null) {
tempB = tempB.next;
}else {
break;
}
}
}
return null;
}

双指针方法

这个双指针非常巧妙的经过依次调换之后,将两个指针指到了相交节点前的相同的位置,简单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
/*
总体思路:双指针
headA=[a1,a2,...,an]与headB=[b1,b2,...,bn]
pA在a1出发,pB在b1出发;当pA到达A链表末尾null就到B开头处重新开始;pB同理
最后两个指针相遇时就是相交的地方
*/
ListNode pA = headA, pB = headB;
while (pA != pB) {
// A指针与B一路往下走,走完了就去另一个链表的头结点,直至相遇
// 这里注意不能用pA.next作为判断,因为遇到空链表会发生空指针异常
// 这里就算有其中一个链表为空或者没相交的也成立(最后为null)
pA = pA == null ? headB : pA.next;
pB = pB == null ? headA : pB.next;
}
// pA==pB
return pA;
}

判断环形链表

142. 环形链表 II - 力扣(LeetCode)

普通方法

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
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode detectCycle(ListNode head) {
HashSet<ListNode> listNodes=new HashSet<>();

ListNode temp=head;
while (temp!=null){
if (!listNodes.add(temp)){
return temp;
}else {
temp=temp.next;
}

}
return null;
}
}

双指针法

将寻找环的过程转换为追击问题,

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 Solution {
public ListNode detectCycle(ListNode head) {
if (head == null) {
return null;
}
ListNode slow = head, fast = head;
while (fast != null) {
slow = slow.next;
if (fast.next != null) {
fast = fast.next.next;
} else {
return null;
}
if (fast == slow) {
ListNode ptr = head;
while (ptr != slow) {
ptr = ptr.next;
slow = slow.next;
}
return ptr;
}
}
return null;
}
}


执行步骤

引入包

axios.min.js · moonshuo/fruit stand - Gitee.com

配置信息

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>01.演示Axios发送普通的参数值给服务器端</title>
<script language="JavaScript" src="script/vue.js"></script>
<script language="JavaScript" src="script/axios.min.js"></script>
<script language="JavaScript">
window.onload=function(){
var vue = new Vue({
"el":"#div0",
//数据区
data:{
uname:"lina",
pwd:"ok"
},
//方法区
methods:{
axios01:function(){
axios({
//需要定义方法,url
method:"POST",
url:"axios01.do",
在这里定义参数
params:{
uname:vue.uname,
pwd:vue.pwd
}
})
//如果成功了,执行这个,
.then(function (value) {
console.log(value);
})
.catch(function (reason) {
//失败则执行这个
console.log(reason);
});
}
}
});
}
</script>
</head>
<body>
<div id="div0">
双向绑定,与vue中的uname和pwd相互绑定
uname:<input type="text" v-model="uname"/><br/>
pwd:<input type="text" v-model="pwd"/><br/>
//点击之后调用axios01函数对象
<input type="button" @click="axios01" value="发送一个带普通请求参数值的异步请求"/>
</div>
</body>
</html>

后台响应

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@WebServlet("/axios01.do")
public class Axios01Servlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");

String uname = request.getParameter("uname");
String pwd = request.getParameter("pwd");

System.out.println("uname = " + uname);
System.out.println("pwd = " + pwd);

response.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
out.write(uname+"_"+pwd);

throw new NullPointerException("这里故意抛出一个空指针异常....");
}
}

客户端发送JSON格式

JSON表示一个数据格式,比如json表示两个学员的信息

[{sid:”s001”,age:18},{sid:”s002” ,age:18}],JSON表达的数据更加简介,更加可以节约网络带宽,而其与原来的差别只是将method中的date转换成了JSON格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
methods:{
axios01:function(){
axios({
method:"POST",
url:"axios01.do",
data:{
uname:vue.uname,
pwd:vue.pwd
}
})
.then(function (value) {
console.log(value);
})
.catch(function (reason) {
console.log(reason);
});
}
}

获取参数的方法

而json格式我们使用request.getParameter进行接受,同时需要导入JSON的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
@WebServlet("/axios02.do")
public class Axios02Servlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

StringBuffer stringBuffer = new StringBuffer("");
//获得对应的读取流
BufferedReader bufferedReader = request.getReader();
String str = null ;
while((str=bufferedReader.readLine())!=null){
//将读到的数据进行追加
stringBuffer.append(str);
}
str = stringBuffer.toString() ;
//获得的值是键值对

//已知 String
//需要转化成 Java 对象

Gson gson = new Gson();
//Gson有两个API
//1.fromJson(string,T) 将字符串转化成java object
//2.toJson(java Object) 将java object转化成json字符串,这样才能响应给客户端

User user = gson.fromJson(str, User.class);

System.out.println(user);
}
}

后台发送JSON数据

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
@WebServlet("/axios03.do")
public class Axios03Servlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

StringBuffer stringBuffer = new StringBuffer("");
BufferedReader bufferedReader = request.getReader();
String str = null ;
while((str=bufferedReader.readLine())!=null){
stringBuffer.append(str);
}
str = stringBuffer.toString() ;

//已知 String
//需要转化成 Java Object

Gson gson = new Gson();
//Gson有两个API
//1.fromJson(string,T) 将字符串转化成java object
//2.toJson(java Object) 将java object转化成json字符串,这样才能响应给客户端

User user = gson.fromJson(str, User.class);
user.setUname("鸠摩智");
user.setPwd("123456");

//假设user是从数据库查询出来的,现在需要将其转化成json格式的字符串,然后响应给客户端
String userJsonStr = gson.toJson(user);
response.setCharacterEncoding("UTF-8");
//MIME-TYPE,告诉浏览器发送的格式
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(userJsonStr);
}
}

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
<script language="JavaScript">
window.onload=function(){
var vue = new Vue({
"el":"#div0",
data:{
uname:"lina",
pwd:"ok"
},
methods:{
axios03:function(){
axios({
method:"POST",
url:"axios03.do",
data:{
uname:vue.uname,
pwd:vue.pwd
}
})
.then(function (value) {
var data = value.data;
// data对应的数据:
// {uname:"鸠摩智",pwd:"ok"}
vue.uname=data.uname;
vue.pwd=data.pwd;

//此处value中的data返回的是 js object,因此可以直接点出属性
//如果我们获取的是一个字符串: "{uname:\"鸠摩智\",pwd:\"ok\"}"

//js语言中 也有字符串和js对象之间互转的API
//string JSON.stringify(object) object->string
//object JSON.parse(string) string->object
})
.catch(function (reason) {
console.log(reason);
});
}
}
});
}
</script>

js与字符串与对象之间的相互转换

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>04.JS中的字符串和Object之间互转的API</title>
<script language="JavaScript">
function hello01(){
/*
//1. js string - > js object
var str = "{\"uname\":\"lina\",\"age\":20}";
var user = JSON.parse(str);
alert(typeof user);
alert(user.uname+"_"+user.age);
*/

//2. js object -> js string
var user = {"uname":"lina","age":99};
alert(typeof user);
var userStr = JSON.stringify(user);
alert(typeof userStr);
alert(userStr);
}
</script>
</head>
<body>
<div id="div0">
<input type="button" value="确定" onclick="hello01()"/>
</div>
</body>
</html>

(来自于尚硅谷|尚硅谷丨2022版JavaWeb教程(全新技术栈,全程实战)_哔哩哔哩_bilibili

01.Axios示例

语法规则

选择器:浏览器根据选择器决定受CSS样式影响的HTML标签

属性:要改变的样式名,并且每一个属性都有一个值

声明:一般有多个声明,需要用分号将每个声明分开

CSS与HTML的结合方式

第一种

在标签的style上设置键值对即可,可读性差,多个标签比较难以定义

1
<div style="color: chartreuse">div1</div>

第二种

在head标签中根据语法规则进行定义,利用style标签进行辅助,但是只能给一个页面使用

1
2
3
4
5
6
7
8
9
10
11
<style type="text/css">
div{
border: 1px solid green;
size: 10px;
}
</style>
</head>
<body >
<div>div1</div>
<div>div2</div>
<div>div3</div>

第三种

单独写一个css文件

1
2
3
4
5
6
<link rel="stylesheet" type="text/css" href="test.css">
</head>
<body >
<div>div1</div>
<div>div2</div>
<div>div3</div>
1
2
3
4
div{
border: 1px solid green;
size: 10px;
}

CSS选择器

标签名选择器

标签名{

属性:值

}

标签名选择器可以被动的选择

id选择器

1
2
3
4
5
6
7
8
9
10
11
12
13
<style>
#id1{
color: blue;
size: 19px;
}
#id2{
color: green;
}
</style>
</head>
<body >
<div id="id1">div1</div>
<div id="id2">div2</div>

类选择器

.class{

属性:值

}

1
2
3
4
5
6
7
8
9
10
11
12
<style>
.class1{
color: blue;
}
.class2{
color: red;
}
</style>
</head>
<body >
<div class="class1">div1</div>
<div class="class2">div2</div>

组合选择器

选择器1,选择器2.。。。{

属性:值

}

1
2
3
4
5
6
<style>
.class1,#id1{
color: blue;
}

</style>

CSS常用样式

颜色:color

宽度高度:width,height

背景颜色:background-color

字体样式:font-size字体大小

边框:border

div居中:margin-left:auto;margin-right:auto

超链接去除下划线:text-decoration:none

列表修饰去除:list-style:none

JavaScript介绍

  1. 交互性:信息的动态交互
  2. 安全性:不允许直接访问本地硬盘
  3. 跨平台性

………

……..

……..

JS中如何自定义对象

Obiect形式自定义对象

var 变量名=new Obiect();

变量名.属性=值;//定义一个属性

变量名.函数名=function(){

}

对象的访问

变量名.属性名或函数名

1
2
3
var  test={};
test.name="阿华";
alert(test.name);

object的第二种定义方式

var 变量名={};

1
2
3
4
5
6
7
8
9
var  test={
name:"阿华",
age: 13,
show:function ()
{
alert(this.name+this.age)
}
};
test.show();

JS中的事件

常用的事件

onload

页面加载完成之后,常用于做页面js代码初始化操作

静态

1
2
3
4
5
6
7
8
9
10
<head><!--头部信息,一般包含三部分内容,title,css标签,js代码-->
<meta charset="UTF-8">
<title>这是一个标题</title>
<script src="js01.js"></script>
</head>
<!--可以在外面定义方法,简化操作-->
<body onload="test.show()">
hello

</body>

动态

1
2
3
4
5
6
7
8
9
10
head><!--头部信息,一般包含三部分内容,title,css标签,js代码-->
<meta charset="UTF-8">
<title>这是一个标题</title>
<script src="js01.js"></script>
</head>
<!--可以在外面定义方法,简化操作-->
<body >
hello

</body>
1
2
3
4
5
6
7
8
9
var  test=new Object();
name="阿华";
age=13;
function hi(){
alert("静态注册")
}
window.onload=function hi(){
alert("动态注册"+this.name+this.age)
}

onclick

单击事件,常用于鼠标的点击相应操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html><!--约束,声明-->
<html lang="zh_CN"><!--表示html的开始,后面表示语言为中文-->
<head><!--头部信息,一般包含三部分内容,title,css标签,js代码-->
<meta charset="UTF-8">
<title>这是一个标题</title>
<script src="js01.js"></script>
</head>
<!--可以在外面定义方法,简化操作-->
<body >
<button onclick="hi()">静态注册</button>
<!--document是javascript提供的一个对象代表所有的对象
id通过id标签获取属性
-->
<button id="button1">动态注册</button>

</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
var  test=new Object();
name="阿华";
age=13;
function hi(){
alert("静态注册")
}
window.onload=function (){
var button= document.getElementById("button1");
button.onclick=function (){
alert("动态调用")
}
}

onblur

常用于输入框失去焦点后,验证其输入内容是否合法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var  test=new Object();
name="阿华";
age=13;
function hi(){
//console是控制台对象,由JavaScript专门用来向浏览器的控制器打印输出用于测试
//log是打印方法
console.log("检测用户名中");
}
window.onload=function (){
var txt=document.getElementById("password");
txt.onblur=function () {
console.log("动态对象")
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html><!--约束,声明-->
<html lang="zh_CN"><!--表示html的开始,后面表示语言为中文-->
<head><!--头部信息,一般包含三部分内容,title,css标签,js代码-->
<meta charset="UTF-8">
<title>这是一个标题</title>
<script src="js01.js"></script>
</head>
<!--可以在外面定义方法,简化操作-->
<body >
用户名:<input type="text" onblur="hi()"><br>
密码:<input type="text" id="password"><br>
</body>
</html>

image-20220328181239182

onchange内容发生改变事件

常用于下拉列表和输入框内容发生改变后的操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var  test=new Object();
name="阿华";
age=13;
function hi(){
//console是控制台对象,由JavaScript专门用来向浏览器的控制器打印输出用于测试
//log是打印方法
alert("国籍已经改变")
}
window.onload=function (){
var obj=document.getElementById("id01")
obj.onchange=function (){
alert("男改变")
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPE html><!--约束,声明-->
<html lang="zh_CN"><!--表示html的开始,后面表示语言为中文-->
<head><!--头部信息,一般包含三部分内容,title,css标签,js代码-->
<meta charset="UTF-8">
<title>这是一个标题</title>
<script src="js01.js"></script>
</head>
<!--可以在外面定义方法,简化操作-->
<body >
请选择你的国籍
<select onchange="hi()">
<option>中国</option>
<option>美国</option>
</select><br>
<select id="id01" >
<option>中国</option>
<option>美国</option>
</select>
</body>
</html>

onsubmit

表单提交事件,验证表单是否合法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var  test=new Object();
name="阿华";
age=13;
function hi(){
alert("表单不合法");
return false;
}
window.onload=function (){
var obj=document.getElementById("id01")
obj.onsubmit=function (){
alert("表单不合法")
return false;
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPE html><!--约束,声明-->
<html lang="zh_CN"><!--表示html的开始,后面表示语言为中文-->
<head><!--头部信息,一般包含三部分内容,title,css标签,js代码-->
<meta charset="UTF-8">
<title>这是一个标题</title>
<script src="js01.js"></script>
</head>
<!--可以在外面定义方法,简化操作-->
<body >
<!--可以阻止不合法的表单提交,return false 可以阻止表单提交-->
<form action="www.baidu.com" method="get" onsubmit="return hi()">
<input type="submit" value="静态注册">
</form>

<!--可以阻止不合法的表单提交,return false 可以阻止表单提交-->
<form action="www.baidu.com" method="get" id="id01">
<input type="submit" value="静态注册">
</form>
</body>
</html>

事件的注册

告诉浏览器,当事件相应后要执行那些代码,叫事件注册

静态事件注册

通过html标签的事件属性直接赋予事件相应后的代码

动态事件注册

是指先通过js代码得到标签的dom对象,然后在通过dom对象.事件名=function(){}这种形式赋予事件响应的代码

Dom模型

把文档中的标签属性文本转换成对象来管理,拥有树形结构

Document对象方法

表示整个html对象

1
document.getElementById
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="zh_CN"><!--表示html的开始,后面表示语言为中文-->
<head><!--头部信息,一般包含三部分内容,title,css标签,js代码-->
<meta charset="UTF-8">
<title>这是一个标题</title>
<script >
function show(){
var obj=document.getElementById("id01");
//alert(obj.type)得到对象的类型
//alert(obj.value)得到值
var userText=obj.value;
//定义规则
var patt=/^\w{5,12}$/;
/*test方法用于测试某个字符串是否匹配规则,如果匹配返回true*/
if (patt.test(userText)){
alert("成功")
}
}
</script>
</head>
<!--可以在外面定义方法,简化操作-->
<body >
<!--可以阻止不合法的表单提交,return false 可以阻止表单提交-->
<form action="www.baidu.com" method="get" >
<input type="text" id="id01" onclick="show()" value="hi">
</form>

</body>
</html>
1
var obj1=document.getElementsByName("name02");//可以得到多个属性值
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
<!DOCTYPE html><!--约束,声明-->
<html lang="zh_CN"><!--表示html的开始,后面表示语言为中文-->
<head><!--头部信息,一般包含三部分内容,title,css标签,js代码-->
<meta charset="UTF-8">
<title>这是一个标题</title>
<script>
function selectAll(){
var hobbies=document.getElementsByName("hobby");
for (var i=0;i<hobbies.length;i++){
hobbies[i].checked=true;
}
}
function selectNo(){
var hobbies=document.getElementsByName("hobby");
for (var i=0;i<hobbies.length;i++){
hobbies[i].checked=false;
}
}
function selectRev(){
var hobbies=document.getElementsByName("hobby");
for (var i=0;i<hobbies.length;i++){
hobbies[i].checked=!hobbies[i].checked;
}
}
</script>
</head>

<body >
兴趣爱好
<input type="checkbox" name="hobby" value="cpp" > c++
<input type="checkbox" name="hobby" value="java" c> java
<input type="checkbox" name="hobby" value="c"> c
<br>
<button onclick="selectAll()">全选</button>
<button onclick="selectNo()">全不选</button>
<button onclick="selectRev()">反选</button>
</body>
</html>
1
var hobbies=document.getElementsByTagName("input");//按照标签属性,选中input标签的属性进行操作

以上三个方法在优先顺序为id优先,name其次

正则表达式

1
2
3
4
5
6
//定义规则
//var patt=new RegExp("e");//表示必须含有e
//var patt=/e/;//表示必须含有e
//var patt=/[abc]/;//表示是否包含任意一个
var patt=/[a-z]/;//表示是否包含a-z任意一个小写字母
var patt=/[0-9]/;//表示是否包含任意数字

元字符

\w,用于查找单个字符

1
var patt=/\w/;

量词

n+,表示是否包含至少一个n

1
var patt=/n+/;

n*,表示字符串中是否包含0个或多个n,最小合格就停止检查

1
var patt=/n*/;

n?,表示字符是否包含0个或1个n

1
var patt=/n?/;

n{3},表示字符是否包含连续三个a

1
var patt=/n{3}/;

n{1,3},最少1,最多3个连续

1
var patt=/n{1,3}/;

n{1,},1个以上

1
var patt=/n{1,}/;

n$,以n为结尾

1
var patt=/n$/;

^n,表示以n开头

1
var patt=/^n/;

^n{3,5}$,表示从头到尾都符合

1
var patt=/^n{3,5}$/;

提示效果

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
<!DOCTYPE html><!--约束,声明-->
<html lang="zh_CN"><!--表示html的开始,后面表示语言为中文-->
<head><!--头部信息,一般包含三部分内容,title,css标签,js代码-->
<meta charset="UTF-8">
<title>这是一个标题</title>
<script >
function show(){
var obj=document.getElementById("id01");
var userText=obj.value;
var patt=/^\w{5,12}$/;
var obj1=document.getElementById("id02");
//innerHtml表示起始标签和结束标签的内容
if (patt.test(userText)){
obj1.innerHTML="用户名合法";
}
else {
obj1.innerHTML="用户名不合法";
}
}
</script>
</head>
<!--可以在外面定义方法,简化操作-->
<body >
<!--可以阻止不合法的表单提交,return false 可以阻止表单提交-->
<form action="www.baidu.com" method="get" >
<input type="text" id="id01" onclick="show()" value="hi">
<span style="color: red" id="id02">
</span>
</form>

</body>
</html>

image-20220329100109666

也可以插入图片作为提示效果

1
obj1.innerHTML="<img width='18px' height='18px' src=\"D:\\壁纸\\蓝天.png\">";

鼠标悬浮与离开

event表示当前发生的对象,srcElement表示事件源

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
<!DOCTYPE html><!--约束,声明-->
<html lang="zh_CN"><!--表示html的开始,后面表示语言为中文-->
<head><!--头部信息,一般包含三部分内容,title,css标签,js代码-->
<meta charset="UTF-8">
<title>水果表格</title>
<link href="test.css" type="text/css" rel="stylesheet">
<script type="text/javascript" src="js01.js"></script>
</head>

<body >
<table border="1" width="800" height="600" id="id01">
<tr >
<th>名称</th>
<th>单价</th>
<th>数量</th>
<th>小计</th>
<th>操作</th>
</tr>
<tr >
<td>苹果</td>
<td onmouseover="shouHand()">5</td>
<td>20</td>
<td>100</td>
<td>×</td>
</tr>
<tr >
<td>西瓜</td>
<td onmouseover="shouHand()">3</td>
<td>10</td>
<td>30</td>
<td>×</td>
</tr>
<tr >
<td>菠萝</td>
<td onmouseover="shouHand()">4</td>
<td>25</td>
<td>100</td>
<td>×</td>
</tr><tr >
<td>榴莲</td>
<td onmouseover="shouHand()">6</td>
<td>12</td>
<td>72</td>
<td>×</td>
</tr><tr >
<td>总计</td>
<td colspan="4" onmouseover="shouHand()">单价</td>
</tr>
</table>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
table{
margin-left: auto;
margin-right: auto;
}
tr{
text-align: center;
}
body{
background-color: skyblue;
}
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
//window表示当前窗口的信息,加载完成绑定事件
//使用此方法可以减少html掺杂js代码
window.onload=function (){
var fruitTable=document.getElementById("id01");
//调用onmouseover方法,不用小括号,否则会返回值
var trEvery=fruitTable.rows;
for (var i=0;i<trEvery.length;i++){
var tr=trEvery[i];
tr.onmouseover=changeBgColor;
tr.onmouseout=clearBgColor;
var cells=tr.cells;
var priceTD=cells[1];
priceTD.onmouseover=shouHand;
}
}
//添加背景颜色
function changeBgColor(){
//event表示当前发生的事件
//event.srcElement表示事件源
//event.srcElement.tagName标签名称
//alert(event.srcElement);
if (event){
var td=event.srcElement;
//获取父元素
var tr=td.parentElement;
tr.style.backgroundColor="green";
tr.style.color="red";
/*
*
* */
}
}
//去除背景颜色
function clearBgColor(){
if (event){
var td=event.srcElement;
var tr=td.parentElement;
//还原为原来的颜色
tr.style.backgroundColor="";
tr.style.color="";
}
}
//当鼠标悬浮在单价单元格,显示手势
function shouHand(){
if (event&&event.srcElement&&event.srcElement.tagName==="TD"){
var td=event.srcElement;
//cursor表示光标形式
td.style.cursor="pointer";
}
}

image-20220329123839917

更改

点击时,单价可以进行更改

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
//window表示当前窗口的信息,加载完成绑定事件
//使用此方法可以减少html掺杂js代码
window.onload=function (){
var fruitTable=document.getElementById("id01");
//调用onmouseover方法,不用小括号,否则会返回值
var trEvery=fruitTable.rows;
for (var i=1;i<trEvery.length-1;i++){
var tr=trEvery[i];
tr.onmouseover=changeBgColor;
tr.onmouseout=clearBgColor;
var cells=tr.cells;
var priceTD=cells[1];
priceTD.onmouseover=shouHand;
//绑定鼠标单价点击
priceTD.onclick=editPrice;

}
}
//更改单价
function editPrice() {
if (event){
var priceTD=event.srcElement;
//判断如果是属于文本节点,判断是否有子节点,
if (priceTD.firstChild&&priceTD.firstChild.nodeType===3) {
//表示设置或者获取当前节点的内部文本
var oldPrice = priceTD.innerText;
//表示设置当前节点的内部html
priceTD.innerHTML = "<input type='text' size='4'/>";
//第一个子节点
var input = priceTD.firstChild;
if (input.tagName === "INPUT") {
input.value = oldPrice;
//选输入框内部文本
input.select();
//失去焦点后操作
input.onblur=updatePrice;
}

}
}
}
//失去焦点,更新单价
function updatePrice(){
if (event){
var input=event.srcElement;
var newPrice=input.value;
//input节点的父节点表示
var priceTD=input.parentElement;
priceTD.innerText=newPrice;
//更新当前行的小计
updateXiaoJi(priceTD.parentElement);
}
}
//更新小计
function updateXiaoJi(tr){
if (tr&&tr.tagName==="TR"){
var tds=tr.cells;
var price=tds[1].innerText;
var count=tds[2].innerText;
var xiaoJi=parseInt(price)*parseInt(count);
tds[3].innerText=xiaoJi;
var fruitTable=document.getElementById("id01");
//更新总计
var rowCount=fruitTable.rows.length;
var totalCount=0;
var rows=fruitTable.rows;
for (var i=1;i<rowCount-1;i++){
var tr=rows[i];
var xj=parseInt(tr.cells[3].innerText);
totalCount=totalCount+xj;
}
rows[rowCount-1].cells[1].innerText=totalCount;
}
}
//添加背景颜色
function changeBgColor(){
//event表示当前发生的事件
//event.srcElement表示事件源
//event.srcElement.tagName标签名称
//alert(event.srcElement);
if (event){
var td=event.srcElement;
//获取父元素
var tr=td.parentElement;
tr.style.backgroundColor="green";
tr.style.color="red";
}
}
//去除背景颜色
function clearBgColor(){
if (event){
var td=event.srcElement;
var tr=td.parentElement;
//还原为原来的颜色
tr.style.backgroundColor="";
tr.style.color="";
}
}
//当鼠标悬浮在单价单元格,显示手势
function shouHand(){
if (event&&event.srcElement&&event.srcElement.tagName==="TD"){
var td=event.srcElement;
//cursor表示光标形式
td.style.cursor="pointer";
}
}

删除指定与限制键盘输入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//判断输入的内容
function checkContent(){
if (event&&event.srcElement&&event.srcElement.tagName==="INPUT"){
var kc =event.keyCode;
//0-9对应编码为48-57
//backspace:8
//enter: 13
if ((kc>=48&&kc<=57)||kc===8||kc===13){
//不满足取消输入
event.returnValue=true;
}else{
event.returnValue=false;
}
if (kc===13){
updatePrice();
}
}
}

Thymeleaf的使用

Thymelaef帮助我们将数据与与html进行结合,渲染到界面上

image-20220402164624274

image-20220404092102023

要将这些水果以list的方式渲染到界面上

使用步骤

导入jar包

image-20220404100437899

写入方法

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
package com.zss.servlets;

import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.WebContext;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.ServletContextTemplateResolver;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
* @author zss
*/
public class ViewBaseServlet extends HttpServlet {

private TemplateEngine templateEngine;

@Override
public void init() throws ServletException {

// 1.获取ServletContext对象
ServletContext servletContext = this.getServletContext();

// 2.创建Thymeleaf解析器对象
ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(servletContext);

// 3.给解析器对象设置参数
// ①HTML是默认模式,明确设置是为了代码更容易理解
templateResolver.setTemplateMode(TemplateMode.HTML);

// ②设置前缀
String viewPrefix = servletContext.getInitParameter("view-prefix");

templateResolver.setPrefix(viewPrefix);

// ③设置后缀
String viewSuffix = servletContext.getInitParameter("view-suffix");

templateResolver.setSuffix(viewSuffix);

// ④设置缓存过期时间(毫秒)
templateResolver.setCacheTTLMs(60000L);

// ⑤设置是否缓存
templateResolver.setCacheable(true);

// ⑥设置服务器端编码方式
templateResolver.setCharacterEncoding("utf-8");

// 4.创建模板引擎对象
templateEngine = new TemplateEngine();

// 5.给模板引擎对象设置模板解析器
templateEngine.setTemplateResolver(templateResolver);

}

protected void processTemplate(String templateName, HttpServletRequest req, HttpServletResponse resp) throws IOException {
// 1.设置响应体内容类型和字符集
resp.setContentType("text/html;charset=UTF-8");

// 2.创建WebContext对象
WebContext webContext = new WebContext(req, resp, getServletContext());

// 3.处理模板数据
templateEngine.process(templateName, webContext, resp.getWriter());
}
}

在web.xml中添加配置

下面中的param-name分别配置上下文参数,而其分别对应ViewBaseServlet的对应的上下文参数

1
2
3
4
5
6
7
8
9
<!-- 在上下文参数中配置视图前缀和视图后缀 -->
<context-param>
<param-name>view-prefix</param-name>
<param-value>/</param-value>
</context-param>
<context-param>
<param-name>view-suffix</param-name>
<param-value>.html</param-value>
</context-param>

继承ViewBaseServlet

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
package com.zss.servlets;

import com.zss.impl.FruitDAOImpl;
import com.zss.object.Fruit;

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.util.List;

public class InquireFruitServlet extends ViewBaseServlet {
@Override
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
req.setCharacterEncoding("UTF-8");
FruitDAOImpl fruitDAO=new FruitDAOImpl();
List<Fruit> list=fruitDAO.inquFruit(new Fruit());
//将变量临时保存到内存中
HttpSession session=req.getSession();
session.setAttribute("fruitList",list);
for (int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
//会将此处的视图名称对应到物理名称
//逻辑视图名称 Fruitshow
//物理视图名称 =前文+逻辑视图名称+后文=/show/Fruitshow.html
super.processTemplate("show/FruitShow",req,resp);
}

}

进行判断,添加对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!--先判断是否为空-->
<tr th:if="${#lists.isEmpty(session.fruitList)}" >
<td colspan="4">对不起,库存为空</td>
</tr>
<!--循环一次,就将session中的数据带入-->
<tr th:unless="${#lists.isEmpty(session.fruitList)}" th:each="fruit : ${session.fruitList}">
<td th:text="${fruit.fid}">1</td>
<td th:text="${fruit.fname}">苹果</td>
<td th:text="${fruit.price}">5</td>
<td th:text="${fruit.fcount}">20</td>
<td th:text="${fruit.remark}"></td>
<td>100</td>
<td>×</td>
</tr>
<tr >

v-bind绑定

vue可以使用对象的方式,来我们常规中js的寻找方法

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
<!DOCTYPE html>
<html lang="zh_CN">
<head>
<meta charset="UTF-8">
<title>这是一个标题</title>
<!--导入vue文件-->
<script src="static/script/vue.js" ></script>
<script >
window.onload=function (){
//创建vue对象
var vue=new Vue({
/*设置键值对*/
"el": "#div0",
data:{
msg: "hello",
uname:"张三"
}
});
}
</script>
</head>

<body >
<div id="div0">
<span>{{msg}}</span>
<!--v-bind表示绑定,也可以省略-->
<input type="text" v-bind:value="uname">
</div>

</body>
</html>

image-20220421160634426

v-model双向绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<script >
window.onload=function (){
//创建vue对象
var vue=new Vue({
/*设置键值对*/
"el": "#div0",
data:{
msg: "hello",
uname:"张三"
}
});
}
</script>
</head>

<body >
<div id="div0">
<span>{{msg}}</span>
<input type="text" v-model:value="msg">
</div>

</body>

动画3

1
<input type="text" v-model.trim="msg">

value值也可以进行省略,而且trim会帮助我们去除前面的空格,而仅仅专注于他的值

条件渲染

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<script >
window.onload=function (){
//创建vue对象
var vue=new Vue({
/*设置键值对*/
"el": "#div0",
data:{
msg: 2,
}
});
}
</script>
</head>

<body >
<div id="div0">
<span>{{msg}}</span>
<input type="text" v-model.trim="msg">
如果msg是偶数,就显示后面的文字,如果不是则不显示,但是注意if与else之间不可以有其他的东西
<div v-if="msg%2==0" style="background-color: red" > haha </div>
<div v-else="msg%2==0" style="background-color: black">nn</div>
v-show在查看源代码中只是将结果给display:none,但是其实还是存在的
<div v-show="msg%2==0" style="background-color: black">nn</div>
</div>

vue迭代

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script language="JavaScript" src="script/vue.js"></script>
<script language="JavaScript">
window.onload=function(){
var vue = new Vue({
"el":"#div0",
data:{
fruitList:[
{fname:"苹果",price:5,fcount:100,remark:"苹果很好吃"},
{fname:"菠萝",price:3,fcount:120,remark:"菠萝很好吃"},
{fname:"香蕉",price:4,fcount:50,remark:"香蕉很好吃"},
{fname:"西瓜",price:6,fcount:100,remark:"西瓜很好吃"}
]
}
});
}
</script>
</head>
<body>
<div id="div0">
<table border="1" width="400" cellpadding="4" cellspacing="0">
<tr>
<th>名称</th>
<th>单价</th>
<th>库存</th>
<th>备注</th>
</tr>
<!-- v-for表示迭代 -->
<tr align="center" v-for="fruit in fruitList">
<td>{{fruit.fname}}</td>
<td>{{fruit.price}}</td>
<td>{{fruit.fcount}}</td>
<td>{{fruit.remark}}</td>
</tr>
</table>
</div>
</body>
</html>

image-20220421163104731

事件驱动

v-on:click

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script language="JavaScript" src="script/vue.js"></script>
<script language="JavaScript">
window.onload=function(){
var vue = new Vue({
"el":"#div0",
data:{
msg:"hello world!"
},
methods:{
myReverse:function(){
this.msg = this.msg.split("").reverse().join("");
}
}
});
}
</script>
</head>
<body>
<div id="div0">
<span>{{msg}}</span><br/>
<!-- v-on:click 表示绑定点击事件 -->
<!-- v-on可以省略,变成 @click -->
<!--<input type="button" value="反转" v-on:click="myReverse"/>-->
<input type="button" value="反转" @click="myReverse"/>
</div>
</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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script language="JavaScript" src="script/vue.js"></script>
<script language="JavaScript">
window.onload=function(){
var vue = new Vue({
"el":"#div0",
data:{
num1:1,
num2:2,
num3:0
},
watch:{
//侦听属性num1和num2
//当num1的值有改动时,那么需要调用后面定义的方法 , newValue指的是num1的新值
num1:function(newValue){
this.num3 = parseInt(newValue) + parseInt(this.num2);
},
num2:function(newValue){
this.num3 = parseInt(this.num1) + parseInt(newValue) ;
}
}
});
}
</script>
</head>
<body>
<div id="div0">
<input type="text" v-model="num1" size="2"/>
+
<input type="text" v-model="num2" size="2"/>
=
<span>{{num3}}</span>
</div>
</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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script language="JavaScript" src="script/vue.js"></script>
<script language="JavaScript">
window.onload=function(){
var vue = new Vue({
"el":"#div0",
data:{
msg:1
},
methods:{
changeMsg:function(){
this.msg = this.msg + 1 ;
}

},
/*vue对象创建之前*/
beforeCreate:function(){
console.log("beforeCreate:vue对象创建之前---------------");
console.log("msg:"+this.msg);
},
/*vue对象创建之后*/
created:function(){
console.log("created:vue对象创建之后---------------");
console.log("msg:"+this.msg);
},
/*数据装载之前*/
beforeMount:function(){
console.log("beforeMount:数据装载之前---------------");
console.log("span:"+document.getElementById("span").innerText);
},
/*数据装载之后*/
mounted:function(){
console.log("mounted:数据装载之后---------------");
console.log("span:"+document.getElementById("span").innerText);
},
beforeUpdate:function(){
console.log("beforeUpdate:数据更新之前---------------");
console.log("msg:"+this.msg);
console.log("span:"+document.getElementById("span").innerText);
},
updated:function(){
console.log("Updated:数据更新之后---------------");
console.log("msg:"+this.msg);
console.log("span:"+document.getElementById("span").innerText);
}
});
}
</script>
</head>
<body>
<div id="div0">
<span id="span">{{msg}}</span><br/>
<input type="button" value="改变msg的值" @click="changeMsg"/>
</div>
</body>
</html>

vue与后台进行绑定的实例

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
window.onload=function(){
var vue = new Vue({
//绑定id为div_cart
el:"#div_cart",
//拥有一个属性cart
data:{
cart:{}
},
methods:{
//拥有三个方法
getCart:function(){
//采用异步的方法,
axios({
method:"POST",
url:"cart.do",
params:{
operate:'cartInfo'
}
})
.then(function (value) {
var cart = value.data ;
vue.cart = cart ;
})
.catch(function (reason) { });
},
addBook:function (bookId){
axios({
method:"POST",
url:"cart.do",
params:{
operate: 'addBook',
bookId:bookId

}
}).then(function (value){
vue.getCart();
}).catch(function (reason){

})
},
reduceBook:function (bookId){
axios({
method:"POST",
url:"cart.do",
params:{
operate: 'reduceBook',
bookId:bookId

}
}).then(function (value){
vue.getCart();
}).catch(function (reason){

})
}

},
mounted:function(){
this.getCart() ;
}
});
}

一个处理的函数

1
2
3
4
5
6
7
8
9
10
11
public String cartInfo(HttpSession session) throws SQLException {
User user=(User)session.getAttribute("currUser") ;
Cart cart= cartItemService.getCart(user);
cart.getTotalCount();
cart.getTotalBookCount();
cart.getTotalMoney();
Gson gson=new Gson();
String cartJsonStr=gson.toJson(cart);

return "json:"+cartJsonStr;
}

判断并进行局部刷新

1
2
3
4
5
6
7
if(methodReturnStr.startsWith("json:")){
String jsonStr=methodReturnStr.substring("json:".length());
PrintWriter out=response.getWriter();
out.print(jsonStr);
//刷新
out.flush();
}

基本用法

回顾

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!DOCTYPE html><!--约束,声明-->
<html lang="zh_CN"><!--表示html的开始,后面表示语言为中文-->
<head><!--头部信息,一般包含三部分内容,title,css标签,js代码-->
<meta charset="UTF-8">
<title>这是一个标题</title>
</head>
<body bgcolor="white" ><!--html的主体内容,
标签拥有基本属性和动态属性
比如:
bgcolor是基本属性
onclick是动态属性
-->
hello
<button onclick="alert('你好啊啊')">按钮</button>

<!--标签分为单标签br:换行,hr横线-->
举头望明月,<br>低头思故乡<hr>
</body>
</html>

标签语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html><!--约束,声明-->
<html lang="zh_CN"><!--表示html的开始,后面表示语言为中文-->
<head><!--头部信息,一般包含三部分内容,title,css标签,js代码-->
<meta charset="UTF-8">
<title>这是一个标题</title>
</head>
<body >
<!--标签不能交叉使用,标签要正确关闭-->
<br/>1
<!--属性必须要有值,属性值必须加上引号,注释不能嵌套-->
<font color="blue">你好</font>

</body>
</html>

常用的标签

font

1
<font COLOR="red" face="宋体" size="5">字体标签,更改颜色等</font>

特殊字符

1
2
我是&lt;br&gt;标签
我是&nbsp;&nbsp;空格

image-20220322200303349

标题标签

1
2
3
4
5
6
7
<!--标题对齐位置-->
<h1 align="left">标题1</h1>
<h2 align="center">标题2</h2>
<h3 align="right">标题3</h3>
<h4>标题4</h4>
<h5>标题1</h5>
<h6>标题6</h6>

超链接

1
2
3
<!--a标签是超链接,拥有两种值,_self代表当前页面,而_blank表示打开新页面进行跳转-->
<a href="www.baidu.com" target="_blank">百度</a>
<a href="www.baidu.com" target="_self" >百度</a>

列表标签

1
2
3
4
5
6
<ul>
<li>第一</li>
<li>第二</li>
<li>第三</li>
<li>第四</li>
</ul>
1
2
3
4
5
6
<ol type="A">
<li>第一</li>
<li>第二</li>
<li>第三</li>
<li>第四</li>
</ol>

img标签

1
2
3
4
5
<!--. 表示当前文件所在的目录 ..表示当前文件所在的上一级目录
border表示设置文件边框
alt属性表示当指定路径找不到图片时,用来代替显示文本的内容
-->
<img src="D:\壁纸\彩云.png" width="50%" height="30%" alt="没有找到">

表格标签

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<table border="1" width="300" height="300" cellspacing="10">
<tr align="center">
<th >11</th>
<th>12</th>
<th>13</th>
</tr> <tr>
<td>21</td>
<td>22</td>
<td>23</td>
</tr> <tr>
<td>31</td>
<td>32</td>
<td>33</td>
</tr>
</table>

image-20220322204319879

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
<table border="1" width="300" height="300" cellspacing="10">
<tr>
<th >11</th>
<th>12</th>
<th>13</th>
</tr>
<tr>
<td colspan="2">21</td>
<td>23</td>
</tr>
<tr>
<td rowspan="2">31</td>
<td>32</td>
<td>33</td>
</tr>
<tr>
<td>41</td>
<td>42</td>

</tr>
<tr>
<td>51</td>
<td>52</td>
<td>53</td>
</tr>

</table>

image-20220322204732063

iframe标签

1
2
3
4
5
6
7
8
9
10
11
<body >
我是一个完整的页面
<iframe src="Hello.html" name="abc"></iframe>
<!--iframe和超链接标签的使用步骤如下
1.在iframe标签中使用name属性定义一个名称
2.在a标签的target属性上设置iframe的name属性值

-->
<a href="Hello.html" target="abc">hello.html页面</a>
<a href="test.html" target="abc">test.html页面</a>
</body>

表单标签(重点)

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
<!--表单就是html中用来收集用户信息所有元素集合,然后把这些信息发送给服务器
input type=text标签是文本输入框
input type=password密码输入框
name可以进行分组,checked是默认
checkbox表示复选
-->
<form>
<label>
用户名称:
<input type="text" value="默认值">
</label><br/>
<label>
密码:
<input type="password">
</label><br/>
<label>
性别:
<input type="radio" name="sex" checked="checked">
</label><label>
<input type="radio" name="sex">
</label><br>
<label>
兴趣爱好:
<input type="checkbox">
</label>java<label>
java
<input type="checkbox">
</label><label>
javascript
<input type="checkbox">
</label>javaee<br>
<label>
国籍:
<select>
<option selected="selected">中国</option>
<option>美国</option>
</select>
</label><br>
<label>
自我评价:<textarea rows="5" cols="100" >默认值</textarea>
</label>
<br>
<input type="button" value="按钮">
<input type="file">
<!--重置-->
<input type="reset" >
<input type="submit" >
</form>

一般情况下表单与嵌套在表格之中,更加美观

表单提交细节
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
<!--
font标签是表单标签
action属性设置提交的服务器地址
method属性设置提交方式GET或Post
表单提交的时候,数据没有发给服务器的三种情况
1.表单没有name属性值
2.提交表单是没有有效的信息,单选复选下拉框中都需要添加value属性
3.表单项不在默认的form标签中
-->
<form ACTION="https://www.baidu.com" method="post">
<label>
用户名称:
<input type="text" value="默认值">
</label><br/>
<label>
密码:
<input type="password">
</label><br/>
<label>
性别:
<input type="radio" name="sex" checked="checked" value="男">
</label><label>
<input type="radio" name="sex">
</label><br>
<label>
兴趣爱好:
<input type="checkbox">
</label>java<label>
java
<input type="checkbox">
</label><label>
javascript
<input type="checkbox">
</label>javaee<br>
<label>
国籍:
<select>
<option selected="selected">中国</option>
<option>美国</option>
</select>
</label><br>
<label>
自我评价:<textarea rows="5" cols="100" >默认值</textarea>
</label>
<br>
<input type="button" value="按钮">
<input type="file">
<!--重置-->
<input type="reset" >
<input type="submit" >
</form>

get与post的不同

GET请求:

  1. 在浏览器中地址是 action属性+?+请求参数
  2. 不安全,其信息在浏览框中都可以看出来
  3. 有数据长度的限制

POST请求:

  1. 浏览器只有服务器地址
  2. 比较安全
  3. 理论上没有数据长度的限制

其他标签

1
2
3
4
5
6
7
8
9
10
11
<!--
div标签默认不在一行
span他的长度是封装数据的长度
p段落标签默认会在段落上方或者下方空出一行
-->
<div>div1</div>
<div>div2</div>
<div>div3</div>
<span>span1</span>
<span>span1</span>
<p>你好呀</p>