Java学习笔记-全栈-Java基础-13-JavaWeb基础

2021-08-18 15:23:06 浏览数 (1)

1. 前后端交互基础

  • 通过超链接跳转传递数据

www.test.com?id=1 与form表单不同的是,uri中的id=1可能是从a标签中直接获取的

  • 通过form表单

1.1 form

  • aciton:请求服务器资源(URL),对应控制器的Mapping
  • name:后端使用,区分唯一(可重名,后端用容器接收)
  • id:前端使用,区分唯一

1.2 引用路径的区别

  • 1)href:指定资源路径,将当前元素与资源之间建立联系
  • 2)src:指向外部资源路径,将该资源应用到当前元素中替换作用
  • 3)action:将本页面的请求传递给目标路径

1.3 RESTful

RESTful是一种前后端交互的规范API。可以理解为“一种约定成俗的编程习惯”。

通常情况,只涉及到前后端请求方法上的约定。

本质上是通过幂等来区分

幂等:概念源于离散数学,用于判断两个关系是否幂等。可理解为“重复操作不改变结果”。

GET、PUT、DELETE都是幂等的

  • 同一个GET无论多少次,获得的资源都是一样的
  • 同一个PUT无论多少次,更新结果依旧一样
  • 同一个DELETE结果都是一样的,那就是“该对象”被删除,不会有副作用。

POST非幂等

  • 同一个POST,请求多次,每个请求都会各自生成一个结果,因此非幂等。

一般情况下,可以按照功能区分。

增:POST 删:DELETE 改:PUT 查:GET

REST-CRUD设计实例

会涉及到一点点后端控制器的知识

功能

请求Uri

对应返回视图

请求方式

访问后的操作

查找所有user

users

userlist

Get

从数据库获得数据,在页面显示所有user

来到添加页面

user

useradd

get

从数据库中获取必要的提示数据(比如可以填写的部门)在页面展示

添加user

user

重定向:userlist

post

提交后从user中实现Dao增加,然后回到userlist显示最新数据

来到修改页面(回显)

User/1

useradd

get

利用PathVariable查询数据,然后将其回显到ueradd页面

修改user

User

重定向:userlist

put

在useradd中修改完毕后put到user,user中更新后返回到userlist显示最新数据

删除一个user

User/1

重定向:userlist

delete

利用PathVariable实现删除,并返回uselist显示最新数据

添加页面和修改页面可以一页两用: 浏览器单击“添加”按钮的时候,只能回显部分必要的提示数据(model为null); 单击“修改”按钮的时候,能通过@pathvariable(Spring注解)查询到对应model:可通过model是否为null,分别展示不同的细节。

由于浏览器版本: 对于put、delete请求,需要在post表单内使用进行请求转换(固定写法) <input type=“hidden” name="_method" value=“delete”/>

在springboot2.x以上,Delete请求还得配置 spring.mvc.hiddenmethod.filter.enabled=true

1.4 GET和POST在报文上的区别

GET:URL后跟参数 POST:报文中空一行,再跟参数

1.5 URL中的特殊字符

序号

特殊字符

含义

十六进制值

1.

URL 中 号表示空格

2.

空格

URL中的空格可以用 号或者编码

3.

/

分隔目录和子目录

/

4.

?

分隔实际的 URL 和参数

?

5.

%

指定特殊字符

%

6.

#

表示书签

#

7.

&

URL 中指定的参数间的分隔符

&

8.

=

URL 中指定参数的值

=

中文会自动进行编码

2. 手写web服务器

流程如下

2.1 获取请求协议

  • 创建serversocket
  • 建立连接,获取客户端的socket
  • 通过输入流获取请求协议
  • 分解请求协议中的内容

2.2 返回响应协议

  • 准备内容
  • 获取字节数组长度
  • 拼接响应协议,注意空格与换行
    • r : return 到当前行的最左边。
    • n: newline 向下移动一行,并不移动左右
    • Linux中n表示回车 换行;
    • Windows中rn表示回车 换行。
    • Mac中r表示回车 换行。
  • 使用输出流输出

封装式代码结构:

  • 内部: - 动态添加content - 根据字节码拼接响应头 - 根据状态码返回相应协议
  • 外部: - 传入content和状态码

2.3 Servlet,XML

  • 通过url获取配置文件,反射成所需的class,再用servlet处理业务。
  • 使用servlet(response,request),解耦代码,实现封装

以上内容作为总结性的文字参考,没有具体代码。 具体代码可查看尚硅谷java300集进行学习

3. JDBC的使用

JDBC只是Java提供的对外接口,具体的实现由数据库公司来实现。比如mysql或Oracle。

3.1 建立连接

代码语言:javascript复制
//1.加载驱动类
Class.forName("com.mysql.jdbc.Driver");

//2.使用manager链接数据库,实际时使用socket进行远程连接,较耗时,真正开发中都是用连接池来管理连接对象
Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/study?serverTimezone=UTC","root","123456");

凡是引入mysql-url的(无论是直接使用JDBC还是框架中引入mysql),都要加上serverTimezone=UTC,否则报错!

3.2 常用接口

  • 1.Statement:用于执行静态的SQL语句并返回它生成结果的对象,
    • a)三种实现类
      • i.Statement:只能发送不带参数的简单sql语句,一般只用于批处理
        • 1.addBatch(“sql语句”),插入语句
        • 2.executeBatch(),执行批量插入
      • ii.preparedStatement:效率高,防止SQL注入,普通需求使用这个
      • iii.CallableStatement:继承自ii,由方法prePareCall创建,用于调用存储过程
    • b)常用Statement方法
      • i.Execute(),运行所有语句,返回是否有结果集
      • ii.executeQuery();运行select语句,然后resultSet结果集
      • iii.executeUpdate();运行insert/update/delete操作,返回更新行数
  • 2.ResultSet接口:
    • a)Statement执行sql语句时返回resultset结果集
    • b)Resultset提供的检索不同类型字段的方法,常用的有
      • i.getString();获得在数据库里面时varchar、char等类型对象
      • ii.getFloat();
      • iii.getDate();
      • iv.getBoolean();
  • 3.依序关系使用对象及连接
    • a)Resultset->statement->connection

3.3 事务

将多个事情组成一个事件集,这个事件集内的所有事件要么同时执行成功,要么同时失败,则称为事务。 是数据库操作的一个执行单元

mysql默认自动提交事务,且每条语句都在单独的事务中 mysql相关操作

  • start transaction 开启事务
  • Rollback 回滚事务
  • Commit 提交事务

ACID

  • A:atomicity原子性
    • 象征事件集是一个整体,不可分可,同生共死(要么…要么…)。
  • C:consistency一致性
    • 无论执行失败或成功,事务执行前后,数据应当是完整的,一致的。
  • I:isolation隔离性
    • 通过数据串行化,使得在同一时间仅有一个请求用于同一数据。保证在并发的情况下,多个用户对同一个数据进行操作的时候,不会相互影响。
  • D:durability持久性
    • 在事务完成以后,该事务对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。即使数据库发生故障也不应该对其有任何影响。

事务的隔离级别

隔离级别分为:

  • (脏读)READ UNCOMMITTED
  • (不可重复读)READ COMMITTED
  • (虚读幻读)REPEATABLE READ
  • (序列化)SERIALIZABLE

不看定义,我们通过名字来理解。 个人以为英语更容易解释:

首先确定一个前提,既然称为“隔离”,那肯定至少有两个对象,一个“被隔离者”,一个“未被隔离者”。 因此,隔离级别都是用于描述两个事务之间的关系。


read uncommitted --> 读取了未提交的(数据)(oracle默认):一个事务读取了另一个事务未提交的数据,这就是“脏”(引用于汉语中的“贪脏了不属于你的东西”)


read committed --> 读取了已提交的(数据):这个比较难理解,事实上他还有个发生前提:先读了一个数据,然后再读的时候,才读取了已提交的。也就是说,在这个过程中,有第二者插手。 例子 A读取到存款还有10元,准备进行消费; B此时消费了5元; A消费的时候,本来刚刚查询的是10元,但是消费的时候(也进行了查询操作)却余额不足两次一样的查询数据却不一样此时就是读取了已提交的,因此也称为“不可重复读”(重复读出现了不一致数据)

mysql默认级别是repeatable read,是可以解决这个问题的,所以初学者可能有疑惑,为什么会发生这种事情。因为这个级别,是允许两个事务同时对数据进行修改的


repeatable read --> 可重复读(mysql默认):既然可重复读,也就是说,当一个事务在进行读取的时候,不允许被其他事务修改。 还是上面那个例子: A读取到存款还有10元,准备进行消费; B此时想进行消费(修改操作),但是由于A正在操作,因此B操作失败。 A成功进行消费。 repeatable read用于锁定修改操作(Update),但是无法锁定插入操作(Insert) 当A进行重复读的时候,若在重复读期间,第三方进行了插入操作(这是被允许的),此时就出现了“幻觉”。 如: A查询存款为10元;但他有点不放心,决定再查一次 此时B存了5元; A刷新查询,发现变成了15!刚才出现幻觉了吗?(幻读)


serializable --> 序列化:此处的序列化跟java的对象序列化不是一个东西。“序列”最常见的含义是“有顺序的列”,因此,他的意思就是指,所有事务按顺序执行,一旦对一个数据开启了事务,其他人就只能排队等候。 这种自然能够排除任何数据错误,同样的,效率最低,几乎不使用


  • 1、READ UNCOMMITTED: 赃读、不可重复读、虚读都有可能发生。
  • 2、READ COMMITTED: 避免赃读。不可重复读、虚读都有可能发生。(oracle默认的)
  • 4、REPEATABLE READ:避免赃读、不可重复读。虚读有可能发生。(mysql默认)
  • 8、SERIALIZABLE: 避免赃读、不可重复读、虚读。 级别越高,性能越低,数据越安全

JDBC中的事务隔离级别设置

设置隔离级别:必须在开启事务之前。 Connection.setTransactionIsolation(int level);

JDBC中的事务

事务开始于:

  • 执行一条DML语句(INSERT、UPDATE或DELETE)
  • 前一个事务结束后,又输入了另一条DML语句

事务结束于:

  • 执行COMMIT(提交)或ROLLBACK(回滚)语句
  • 执行DDL语句(create、alter、drop和 truncate),将自动执行COMMIT。
  • 断开数据库连接
  • 执行DML语句,该语句失败了,则会自动执行ROLLBACK。

注意: JDBC默认自动提交事务,若设为手动提交,只有提交之后才会真正执行DDL语句。 因此: commit()才是事务的结束,execute和statement不是。

一般来说,通过try-catch,在catch中回滚。

JDBC使用事务

  • Connection.setAutoCommit(false); 开启事务
  • Connection.rollback(); 回滚事务
  • Connection.commit(); 提交事务

Savepoint

java.sql提供的方法,通过Savepoint可以实现部分提交、部分回滚

需求:AB(必须),CD(可选)

代码语言:javascript复制
Connection conn = null;
Savepoint savepoint = null;  //保存点,记录操作的当前位置,之后可以回滚到指定的位置。(可以回滚一部分)
try{
  //1 获得连接
  conn = ...;
  //2 开启事务
  conn.setAutoCommit(false);
  A
  B
  savepoint = conn.setSavepoint();
  C
  D
  //3 提交事务
  conn.commit();
} catche(){
  if(savepoint != null){   //CD异常
     // 回滚到CD之前
     conn.rollback(savepoint);
     // 提交AB
     conn.commit();
  } else{   //AB异常
     // 回滚AB
     conn.rollback();
  }
}

3.4 JDBC中的特殊对象

CLOB

Character Large Object,顾名思义,用于存储大量的文本数据。

大字段有些特殊,不同的数据库处理的方式不一样,大字段的操作常常以流的方式来处理,而非一般的字段,一次即可读出数据。

mysql中对应的相关类型:

  • TINYTEXT
  • TEXT
  • MEDIUMTEXT
  • LONGTEXT

BLOB

Binary Large Object,顾名思义,用于存储大量的二进制数据

也是通过流来处理

mysql中对应的相关类型:

  • TINYBLOB
  • BLOB
  • MEDIUMBLOB
  • LONGBLOB

3.5 数据库连接池

原理

"池"这个概念,不应该陌生。接触最早的“池”就是常量池:用于存放常量,当程序使用该常量的时候,直接从常量池中取,而不是新建一个。因此多个常量(如String指向的字符串常量)具有相同地址。 因此,“连接池”是用于存放数据库连接的,当程序需要连接的时候,就从连接池中获取,不需要就放回。

目的:与数据库建立连接实际上非常耗费资源,提前构建连接池能够大大提高数据交互效率。

常用数据库连接池

DBCP(DataBase Connection Pool)数据库连接池,是java数据库连接池的一种,由Apache开发,通过数据库连接池,可以让程序自动管理数据库连接的释放和断开。

C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate,Spring等。

由于Spring的缘故,因此博主学习的时候用的是C3P0。但是不同的连接池的使用方法基本上没啥差别。

目前在用阿里的druid

使用步骤:

  1. 添加jar包
  2. 编写配置文件c3p0-config.xml,放在classpath中,或classes目录中
代码语言:javascript复制
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
  <default-config>
	<property name="driverClass">com.mysql.jdbc.Driver</property>
	<property name="jdbcUrl">jdbc:mysql://localhost:3306/day12</property>
	<property name="user">root</property>
	<property name="password">123456</property>
    <property name="initialPoolSize">10</property>
    <property name="maxIdleTime">30</property>
    <property name="maxPoolSize">100</property>
    <property name="minPoolSize">10</property>
  </default-config>
</c3p0-config>

配置的属性可以通过顾名思义理解(如maxPoolSize就是指池中最大连接数),在此不再赘述,不懂得可以百度。

<property name=“driverClass”>com.mysql.jdbc.Driver</property> 这句话在高版本的框架会警告已经完成自动配置,可以不写

3. 编写工具类,使用

此处无需耗费过多的时间,掌握连接池的原理,以及xml配置生效原理即可,后面学习框架配置会更加简单。

0 人点赞