当前位置: 首页 > news >正文

东营市建设工程网官网/慈溪seo排名

东营市建设工程网官网,慈溪seo排名,贸易公司取什么名字,深圳龙华政府在线博客系统的具体实现 文章目录博客系统的具体实现软件开发的基本流程具体实现的八大功能数据库设计创建数据库操作数据库引入依赖封装DataSource创建实体类将JDBC增删改查封装起来实现博客列表页web.xml的配置文件实现博客系统的展示功能登录功能强制要求用户登录显示用户信息退…

博客系统的具体实现

文章目录

  • 博客系统的具体实现
    • 软件开发的基本流程
    • 具体实现的八大功能
    • 数据库设计
    • 创建数据库
    • 操作数据库
      • 引入依赖
      • 封装DataSource
      • 创建实体类
      • 将JDBC增删改查封装起来
    • 实现博客列表页
      • web.xml的配置文件
    • 实现博客系统的展示功能
    • 登录功能
    • 强制要求用户登录
    • 显示用户信息
    • 退出登录状态
    • 发布博客
    • 删除博客
  • 在Linux上部署博客系统
    • 安装jdk
    • 安装tomcat
    • 安装mysql
      • 使用yum安装必要的包
      • 启动
    • 正式部署

在正式写后端程序之前,我已经将博客系统的前端最基本的页面写好,详情可以见我的gitee

https://gitee.com/dengchuanfei/vscode_demo/tree/master/blog_system

软件开发的基本流程

  • 可行性分析
  • 需求分析
  • 概要设计
  • 详细设计
  • 编码
  • 测试
  • 发布

具体实现的八大功能

  • 实现博客列表的展示功能
  • 实现博客详情的展示功能
  • 登录功能
  • 强制用户登录
  • 显示用户的信息
  • 实现注销
  • 发布博客
  • 删除博客

数据库设计

写之前首先要进行规划,要做到“谋定而后动”

首先进行“数据库设计”,也就是想清楚需要几个库,几张表,每个表长啥样(属性是干什么的,是什么类型)

需要找到实体,然后分析实体之间的关联关系,再思考表的属性

这里业务比较简单,所以只需要两张表

博客表blog (blogId, tittle, content, postTime, userId)

用户表user (userId username password)

创建数据库

先建一个数据库,往里面添加数据

-- 拿到的数据库很可能并不干净,所以创建之前要确保之前没有同名的,还要删除同名的,注意:这是十分危险的操作,所以务必谨慎使用!!!
create database if not exists java_blog_system;
use java_blog_system;
drop table if exists blog;
-- 注意这里添加属性是使用()
create table blog(blogId int primary key auto_increment,title varchar(256),content text,postTime datetime,--userId是文章作者的IDuserId int);drop table if exists user;
create table  user
(userId   int primary key auto_increment,username varchar(50),password varchar(50)
);
-- 添加几个数据,测试一下
insert into blog values(null,"这是第一篇博客","从今天开始我要好好写代码,好好上课",now(),1);
insert into blog values(null,"这是第二篇博客","我要好好写代码,好好上课",now(),1);insert into user values(null, "zhangsan","123");
insert into user values(null, "lisi","123");

这里设计的content是text类型的,text能放64KB的内容,一般是够博客使用的了,博客中的截图和博客文字不是存储在一起的,所以不用担心截图放不下

注意: 在SQL中的注释是 “-- ”,在–后面还有一个空格!

操作数据库

引入依赖

首先要先引入maven依赖,在.xml的中引入依赖

去中央仓库中,搜索servlet API(3.1.0),mysql connect Java(5.1.49), jackson Databind(2.13.4.2)(将JSON格式进行转换)

封装DataSource

由于DataSource只有一份,所以使用单例模式来实现会比较好

import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;import javax.sql.DataSource;
import javax.xml.crypto.Data;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;//使用这个类来封装DataSource,使用单例模式(懒汉模式 + 多线程判断)
//实现数据库的连接和断开
public class DBUtil {private static volatile  DataSource dataSource = null;public static DataSource getDataSource() {//第一次判断是否需要加锁if (dataSource == null) {synchronized (DBUtil.class) { //针对类对象加锁//第二次判断是会否需要new对象if (dataSource == null) {dataSource  = new MysqlDataSource();((MysqlDataSource)dataSource).setURL("jdbc:mysql://127.0.0.1:3306/java_blog_system?characterEncoding=utf8&&useSSL=false");((MysqlDataSource)dataSource).setUser("root");((MysqlDataSource)dataSource).setPassword("1111");}}}return dataSource;}//建立连接private  static Connection getConnection() throws SQLException {return getDataSource().getConnection();}//关闭连接//建立连接的顺序是connection statement resultSet,所以关闭的顺序是反着的private static void close(Connection connection, Statement statement, ResultSet resultSet) {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();}}}}

image-20230219120213497

在关闭资源的时候, 要是像这样直接使用依次try catch就会导致一旦上面的抛出异常,下面的就不会被执行到了,此时就会导致资源泄漏,很严重

要是使用的是throws相当于上面的情况,后面的代码还是不会执行,导致资源泄漏

所以还是应该 细分一下,多用几个try catch,来保证3个对象都关闭了

创建实体类

一个实体类对象就对应表中的一条记录

表中的属性怎么写,实体类就这么写

这里需要创建2个实体类 User 和 Blog

里面需要有属性和getter和setter方法, IDEA快捷键是alt + fn + insert

import java.security.Timestamp;public class Blog {private int blogId;private String tittle;private String content;//mysql中的datetime和timestamp类型在java中都是使用Timestamp表示的private Timestamp postTime;private int userId;public int getBlogId() {return blogId;}public void setBlogId(int blogId) {this.blogId = blogId;}public String getTittle() { return tittle;}public void setTittle(String tittle) {this.tittle = tittle;}public String getContent() {return content;}public void setContent(String content) {this.content = content;}public Timestamp getPostTime() {return postTime;}public void setPostTime(Timestamp postTime) {this.postTime = postTime;}public int getUserId() {return userId;}public void setUserId(int userId) {this.userId = userId;}
}
public class User {private int userId;private String username;private String password;public int getUserId() {return userId;}public void setUserId(int userId) {this.userId = userId;}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;}
}

将JDBC增删改查封装起来

​ 这里创建的是BlogDao和UserDao类,这里的DAO是 Data Acess Object 数据访问对象

也就是说访问数据库的操作就可以使用这几个DAO对象来进行

import java.sql.*;
import java.util.ArrayList;
import java.util.List;
//封装关于博客的相关操作
public class BlogDao {//插入博客--发布博客public void insert(Blog blog) {Connection connection = null;PreparedStatement statement = null;//此处只要判断改变的行数是不是1就行了,所以没有resultSettry {//1.建立连接connection = DBUtil.getConnection();//2.构造SQL//sql对应着blog的属性//blogId tittle content postTime userIdString sql = "insert into blog values (null, ? , ?, now(), ?)";statement = connection.prepareStatement(sql);statement.setString(1, blog.getTittle());statement.setString(2, blog.getContent());statement.setInt(3, blog.getUserId());//3.执行sqlint ret = statement.executeUpdate();//executeUpdate的返回值是修改的行数if (ret != 1) {System.out.println("博客插入失败!");} else {System.out.println("博客插入成功!");}//4.释放相关的资源--但是这里还是不适合,要是上面代码抛异常了,这里就会导致资源没有释放,资源泄露} catch (SQLException e) {e.printStackTrace();} finally{//方法哦finally就一定会执行到了,但是connection和statement是局部变量,所以就将这两个放到最外面,先置为nullDBUtil.close(connection, statement, null);//这里没有涉及到resultSet,所以填null}}//查询一个博客--博客详情页public Blog selectOne(int blogId) {Connection connection = null;PreparedStatement statement = null;ResultSet resultSet = null;try {//1.建立连接connection = DBUtil.getConnection();//2.构造SQLString sql = "select * from blog where blogId = ?";statement = connection.prepareStatement(sql);statement.setInt(1, blogId);//3.执行SQLresultSet = statement.executeQuery();//遍历结果集合if (resultSet.next()) {Blog blog = new Blog();blog.setBlogId(resultSet.getInt("blogId"));blog.setTittle(resultSet.getString("tittle"));blog.setContent(resultSet.getString("content"));blog.setPostTime(resultSet.getTimestamp("postTime"));blog.setUserId(resultSet.getInt("userId"));return blog;}} catch (SQLException e) {e.printStackTrace();}finally {//关闭资源DBUtil.close(connection,statement,resultSet);}return null;//要是没有找到直接返回null就行}//查询所有博客--博客展示页public List<Blog> selectAll() {List<Blog> blogs = new ArrayList<>();Connection connection = null;PreparedStatement statement = null;ResultSet resultSet = null;try {//1.建立连接connection = DBUtil.getConnection();//2.构造SQLString sql = "select * from blog";statement = connection.prepareStatement(sql);//3.执行SQLresultSet = statement.executeQuery();//遍历结果集合,这里使用的是while循环来寻找while(resultSet.next()) {Blog blog = new Blog();blog.setBlogId(resultSet.getInt("blogId"));blog.setTittle(resultSet.getString("tittle"));blog.setContent(resultSet.getString("content"));blog.setPostTime(resultSet.getTimestamp("postTime"));blog.setUserId(resultSet.getInt("userId"));blogs.add(blog);//将所有搜到的blog都添加到blogs中} } catch (SQLException e) {e.printStackTrace();}finally {//4.关闭资源DBUtil.close(connection,statement,resultSet);}return blogs;}//删除博客public void delete(int blogId) {Connection connection = null;PreparedStatement statement = null;try {//1.建立连接connection = DBUtil.getConnection();//2.构造SQLString sql = "delete from blog where blogId = ?";statement = connection.prepareStatement(sql);statement.setInt(1, blogId);//3.执行sqlint ret = statement.executeUpdate();//executeUpdate的返回值是修改的行数if (ret != 1) {System.out.println("博客删除失败!");} else {System.out.println("博客删除成功!");}//4.释放相关的资源--但是这里还是不适合,要是上面代码抛异常了,这里就会导致资源没有释放,资源泄露} catch (SQLException e) {e.printStackTrace();} finally{//方法哦finally就一定会执行到了,但是connection和statement是局部变量,所以就将这两个放到最外面,先置为nullDBUtil.close(connection, statement, null);//这里没有涉及到resultSet,所以填null}}
}
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;public class UserDao {//根据用户名来查询用户--登录模块//隐含条件:用户名必须要是唯一的public User selectByName(String username) {Connection connection = null;PreparedStatement statement = null;ResultSet resultSet = null;try {//1.建立连接connection = DBUtil.getConnection();//2.构造SQLString sql = "select * from blog where username = ?";statement = connection.prepareStatement(sql);statement.setString(1, username);//3.执行SQLresultSet = statement.executeQuery();//遍历结果集合if (resultSet.next()) {User user = new User();user.setUserId(resultSet.getInt("userId"));user.setUsername(resultSet.getString("username"));user.setPassword(resultSet.getString("password"));return user;}} catch (SQLException e) {e.printStackTrace();}finally {//关闭资源DBUtil.close(connection,statement,resultSet);}return null;}//根据用户ID来查询用户--在获取用户信息的时候会用到public User selectById(int userId) {Connection connection = null;PreparedStatement statement = null;ResultSet resultSet = null;try {//1.建立连接connection = DBUtil.getConnection();//2.构造SQLString sql = "select * from blog where userId = ?";statement = connection.prepareStatement(sql);statement.setInt(1, userId);//3.执行resultSet =  statement.executeQuery();if (resultSet.next()) {User user = new User();user.setUserId(resultSet.getInt("userId"));user.setUsername(resultSet.getString("username"));user.setPassword(resultSet.getString("password"));return user;}} catch (SQLException e) {e.printStackTrace();}finally {//4.关闭资源DBUtil.close(connection,statement,resultSet);}return null;}
}

将上面的几个函数的实现都看明白,其实JDBC的操作都是差不多的

下面主要是服务端和客户端的代码实现,由于服务端的代码比较长,就只有贴出核心功能的客户端代码

详细的客户端 服务端代码将会在文章最后给出

实现博客列表页

将之前写的前端代码复制到webapp目录下

image-20230219161534473

web.xml的配置文件

<!DOCTYPE web-app PUBLIC"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN""http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app><display-name>Archetype Created Web Application</display-name>
</web-app>

实现博客展示,是从数据库中读取数据,然后写到前端网页上

在博客列表页,需要做一个很重要的事情,页面在加载的时候通过ajax发起HTTP请求,从服务端获取到博客列表的数据

所以需要实现想好发什么样的请求,返回什么样的响---->约定前后端接口

[请求]
GET /blog[响应]
[{blogId: 1,title: "第一篇博客",content: "博客正文",userId: 1,postTime: "2021-07-07 12:00:00"},{blogId: 2,title: "第二篇博客",content: "博客正文",userId: 1,postTime: "2021-07-07 12:10:00"},...
]

由页面发起请求,后服务端进行响应

import com.fasterxml.jackson.databind.ObjectMapper;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("/blog")
public class BlogServlet extends HttpServlet {private ObjectMapper objectMapper = new ObjectMapper();@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {BlogDao blogDao = new BlogDao();List<Blog> blogs = blogDao.selectAll();resp.setContentType("application/json;charset=utf8");resp.getWriter().write(objectMapper.writeValueAsString(blogs));}
}

在之前已经写过blog_list.html页面上进行修改


<!-- 发送ajax从服务端上获取数据 -->
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<script>//在页面加载的时候,通过ajax给服务端发送消息,获取到博客列表信息,并且显示在页面上function getBlog(){$.ajax({type:'get',url:'blog',success:function(body){//获取到的body就是一个js对象数组,每个元素就是一个js对象,根据这个对象来构造一个div//1.把原来.right里面原有的内容清空,替换成从数据库服务端拿到的let rightDiv=document.querySelector('.container-right');rightDiv.innerHTML='';//清空原有数据//2.遍历body,构造出一个个blogDivfor(let blog of body){let blogDiv=document.createElement('div');//针对blogDiv设置一个属性,类名设为blogblogDiv.className='blog';//构造内部元素//构造标题let titleDiv=document.createElement('div');titleDiv.className='title';titleDiv.innerHTML=blog.title;//作为子元素添加进去blogDiv.appendChild(titleDiv);//构造发布时间let dateDiv=document.createElement('div');dateDiv.className='date';dateDiv.innerHTML=blog.postTime;blogDiv.appendChild(dateDiv);//构造摘要let descDiv=document.createElement('div');descDiv.className='desc';descDiv.innerHTML=blog.content;blogDiv.appendChild(descDiv);//链接 查看全文(这里用的是a标签)let a=document.createElement('a');a.innerHTML='查看全文 &gt;&gt;';//希望点击后能跳转到博客详情页//跳转要告知哪个博客的详情页a.href='blog_detail.html?blogId='+blog.blogId;blogDiv.appendChild(a);//把blogDiv挂到dom树上rightDiv.appendChild(blogDiv);}},error:function(){alert("获取博客列表失败");}});}getBlog();
</script>

image-20230220165053186

这里的时间很明显就是一个时间戳,并不直观,所以还是要改的,通过fiddler可以看到返回的响应就是时间戳的格式,所以也就是要将get方法的返回值变成String类型的时间格式

此处就要使用SimpleDateFormat类了

public String getPostTime() {//使用IDEA提供的原生的,返回的是时间戳,所以需要改一下返回值//使用SimpleDateFormat来将时间戳转换成指定的时间格式//这里的参数标准格式建议查一下,因为在不同的语言中表示放方式是不一样的SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");return simpleDateFormat.format(postTime);}

image-20230220170449431

时间的格式确实改好了,但是发现最上面的博客确实比较老的博客,不符合常规的博客展示思路

此时只要在BlogDao中将selectAll的搜索改成select * from blog order by postTime desc 按时间降序排列就行了

要是正文很长,在博客展示页就应该显示一部分文章,此时就对内容进行截断

在BlogDao 中进行content的判断和截断:

String content = resultSet.getString("content");
if(content.length() > 100){content = content.substring(0,100) + "......";
}
blog.setContent(content);

实现博客系统的展示功能

在展示的时候,发送ajax请求来访问服务端,获取到服务端返回的响应之后,填充到博客的详情页面中

@WebServlet("/blog")
public class BlogServlet extends HttpServlet {private ObjectMapper objectMapper = new ObjectMapper();@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setContentType("application/json;charset=utf8");BlogDao blogDao = new BlogDao();String  blogId = req.getParameter("blogId");if(blogId == null){//说明是博客列表页发起的请求List<Blog> blogs = blogDao.selectAll();resp.getWriter().write(objectMapper.writeValueAsString(blogs));}else{//说明是博客详情页发起的请求Blog blog = blogDao.selectOne(Integer.parseInt(blogId));resp.getWriter().write(objectMapper.writeValueAsString(blog));}}
}

客户端核心代码:

function getBlog(){$.ajax({type:'get',url:'blog' + location.search,success : function(body){let h3 =  document.querySelector('.blog-detail>h3');h3.innerHTML = body.title;let dateDiv = document.querySelector('.blog-detail>.blog-date');dateDiv.innerHTML = body.postTime; //方法1://let contentDiv=  document.querySelector('#content');//contentDiv.innerHTML = body.content;//方法2://此处使用editor.md来进行渲染,主要是后面实现博客编辑的时候使用editormd.markdownToHTML('content', {markdown: body.content}); }});}getBlog();

关于前端代码:

  1. 要是想显示具体的哪篇博客,就要知道具体的博客id,所以这里写URL的时候将location.search添加上去了
  2. 为什么这里要使用方法2,而不是方法1,主要是因为博客是以markdown的形式来写的,所以渲染的时候也要以markdown格式来渲染

登录功能

所谓的登录功能就是在登录之后跳转到博客列表页

服务端代码:

其实登录操作就是先根据请求获取到用户输入的账号密码,之后进行判断,看看用户输入的账号密码是不是符合要求,要是符合的话,再查询数据库,要是账号密码都是正确的,就创建出一个会话,重定向到博客列表页

@WebServlet("/login")
public class LoginServlet extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//1.获取用户名和密码req.setCharacterEncoding("utf-8");String username = req.getParameter("username");String password = req.getParameter("password");if (username == null || username.equals("") || password == null || password.equals("")) {//用户名或者密码不存在(为空)resp.setContentType("text/html;charset= utf8");resp.getWriter().write("用户名或者密码错为空,登录失败!");return;}//2.查询数据库,查看用户名或者密码是否正确UserDao userDao = new UserDao();User user = userDao.selectByName(username);if (user == null || !user.getPassword().equals(password)) {//用户不存在或者密码不正确resp.setContentType("text/html;charset= utf8");resp.getWriter().write("用户名或者密码错误,登录失败!");return;}//3.要是登录成功,就创建一个会话HttpSession session = req.getSession(true);//在会话中保存user,方便后面知道当前的user是谁session.setAttribute("user", user);//4.构造302响应报文(重定向)resp.sendRedirect("blog_list.html");}
}

客户端核心代码:

 <div class="login-container"><form action="login" method="post"><!-- 实现登录对话框 --><div class="dialog"><h3>登录</h3><div class="row"><span>用户名</span><input type="text"id="username" name = "username"></div><div class="row"><span>密码</span><input type="password" id ="password" name = "password">    </div><div class="row"><input class="login-btn"  value="登录" type = "submit"> </div></div></form></div>

强制要求用户登录

在博客系统中要求要先登录之后才能查看博客和编辑博客,所以要检查用户的登录状况,要是没有登录的话就自动跳转到登录页面

服务端核心代码:

首先要判断当前有没有创建出会话,要是当前存在session并且user也存在,说明已经登录了,就不进行操作,返回一个状态码200就行了,要是没有session或者user就说明当前没有登录,重定向到登录页面

@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//1.首先先获取一下会话//getSession的参数是false,就是说要是没有会话,也不用创建会话HttpSession session =req.getSession(false);if(session == null){//当前没有会话,说明没有登录resp.setStatus(403);return;}//为什么要判断user是否存在,其实还是会存在"有session但是没有user对象"这种情况的,这种情况会在后面退出登录的时候出现,所以此处一定要判断user对象是否存在User user = (User) session.getAttribute("user");if (user == null) {//说明此时虽然有会话,但是没有user对象,还是不行resp.setStatus(403);return;}//当前已经是登录状态,其实这行代码不写也行,但是写上更清楚resp.setStatus(200);}

客户端核心代码 :

function checkLogin() {$.ajax({type:'get',url:'login',success: function(body) {// 成功不做处理},error: function() {// 失败就会强行跳转到登录页面location.assign('login.html');}})
}

显示用户信息

当李四登录进去之后看到张三的文章,之后点了进去,此时就要修改一下用户的信息

这里的服务端逻辑要分成两种情况:

  1. 要是当前用户是在博客列表页,登录的信息就在session中
  2. 要是当前用户是在博客详情页中,就要求数据库中查询文章作者的信息
import com.fasterxml.jackson.databind.ObjectMapper;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("/userInfo")
public class UserInfoServlet extends HttpServlet {private  ObjectMapper objectMapper = new ObjectMapper();@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//首先获取用户信息String blogId = req.getParameter("blogId");//getParameter是用来获取表单中的值的,返回值是String,所以此时的blogId就变成了String类型if (blogId == null) {//博客ID为0说明当前是在列表页,登录的用户信息在session里面,所以从session中拿getUserInfoFromSession(req,resp);}else{//存在博客ID说明当前是在博客展示页,要获取作者信息,就要查询数据库,不能从session中拿,因为文章作者可能不是自己,所以要从数据库中拿getUserInfoFromDB(req,resp, Integer.parseInt(blogId));//将blogId转换成int类型}}private void getUserInfoFromDB(HttpServletRequest req, HttpServletResponse resp, int blogId) throws IOException {//先根据blogId查询blog对象,获取到userId(作者是谁)// 根据user查询对应的User对象即可BlogDao blogDao = new BlogDao();Blog blog = blogDao.selectOne(blogId);if (blog == null) {//这种情况是: blogId是随便写的,数据库中查不到,是不符合要求的resp.setStatus(404);resp.setContentType("text/html;charset=utf8");resp.getWriter().write("blogId不存在");return;}UserDao userDao = new UserDao();User user = userDao.selectById(blog.getUserId());if (user == null) {resp.setStatus(404);resp.setContentType("text/html;charset=utf8");resp.getWriter().write("user不存在");return;}//排除了上面两种错误情况,接下来只有将user以JSON的形式返回就行了user.setPassword("");resp.setContentType("application/json;charset=utf8");resp.getWriter().write(objectMapper.writeValueAsString(user));}private void getUserInfoFromSession(HttpServletRequest req, HttpServletResponse resp) throws IOException {//首先先获取会话HttpSession session = req.getSession(false);if (session == null) {resp.setStatus(403);resp.setContentType("text/html;charset=utf8");resp.getWriter().write("当前未登录!");return;}//获取user对象User user = (User) session.getAttribute("user");if (user == null) {resp.setStatus(403);resp.setContentType("text/html;charset=utf8");resp.getWriter().write("当前未登录!");return;}//在获取到user对象之后为了安全起见,将密码设置为空//以JSON的形式返回响应,所以要提前创建objectMapperuser.setPassword("");resp.setContentType("application/json;charset=utf8");resp.getWriter().write(objectMapper.writeValueAsString(user));}
}

服务端核心代码:

//针对博客列表页,获取到当前用户的信息function getUserInfo(){$.ajax({type:'get',url:'userInfo',success : function(body){//修改头像let h3 = document.querySelector('.container-left>.card>h3')h3.innerHTML = body.username;}})}getUserInfo();

退出登录状态

在页面上一个注销按钮,要求实现点击一下按钮就能退出当前的账号

服务端代码:

后端服务端在收到请求之后,将当前会话的信息删除,然后重定向到登录页面即可

//实现注销功能(退出登录状态)
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("/logout")
public class LogoutServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {HttpSession session = req.getSession(false);if (session == null) {resp.setStatus(403);return;}//删除user对象,并重定向到登录页面session.removeAttribute("user");resp.sendRedirect("login.html");}
}

这里需要注意的是: 在登录状态下,既有session又有user属性,要是想要删除用户的信息,只要删除其中一个就行了,但是这里选择的是删除user属性,因为没有提供删除session的api,并不是很好删除session

服务端代码:

<a href="logout">注销</a>

只要将注销按钮变成一个链接形式即可

发布博客

发布博客的意思是在用户写完博客之后,点击提交按钮,服务端收到请求之后,在数据库中添加一条记录,并且在最后还会跳转到博客列表页

有一个前提: 用户要写上了标题和正文之后点击提交按钮才能提交

所以还要判断一下是否用户写了标题和正文

服务端代码:

接着在原本的BlogServlet类中写

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//使用这个post方法来实现创建新的博客//1.检查一下用户的登录状态和用户信息,要是未登录就不能创建新的博客HttpSession session = req.getSession(false);if (session == null) {resp.setStatus(403);resp.setContentType("text/html;charset=utf8");resp.getWriter().write("当前未登录,不能创建博客!");return;}//检查是否存在user对象User user = (User) session.getAttribute("user");if (user == null) {resp.setStatus(403);resp.setContentType("text/html;charset=utf8");resp.getWriter().write("当前未登录,不能创建博客!");return;}//2.当前已经登录,获取请求的参数(博客的标题和内容)req.setCharacterEncoding("utf8");//将请求变成utf8的格式,防止乱码String title = req.getParameter("title");String content = req.getParameter("content");//3.构造Blog对象,并插入到数据库中Blog blog = new Blog();blog.setTitle(title);blog.setContent(content);//博客的blogId是自增主键,所以不用专门指定设置,postTime是now()函数,所以也不用指定blog.setUserId(user.getUserId());BlogDao blogDao = new BlogDao();blogDao.insert(blog);//4.插入新的博客之后重定向到博客列表页resp.sendRedirect("blog_list.html");//有一个疑问:前面已经实现了未登录状态下是不能进入博客编辑页的,那么此时为什么还要先判断一下是否登录呢?//1.要是有人使用postman直接构造post请求,直接绕开了登录,插入数据怎么办?//2.在上面的代码中,要想给新的博客设置userId需要使用user.getUserId(),所以前面要保证存在user对象//结合上面的两点理由,加上登录判断是很有必要的
}

客户端核心代码:

使用form表单来提交请求

<!-- 这个编辑页的版心 --><div class="blog-edit-container" style="height: 100%"><!-- 套上一个form标签 --><form action="blog" method="post" style="height: 100%"><!-- 标题的编辑区 --><div class="title"><input type="text" id = 'blog-title' placeholder="请在这里输入文章的标题" name = "title"><input id = "submit" value="发布文章" type="submit">    </div><!-- 正文的编辑区 --><div id = "editor"><!-- editor.md规定,要想使用form表单来提交数据,就要按照下面的标准来写 --><textarea name="content" style="display: none"></textarea></div></form></div>

删除博客

要是想要删除博客,在不考虑管理员的情况下,只能由博客的作者自己来删除

所以就要判断当前登录的用户是不是博客的作者,要是不是的话,他是没有权限删除别人的博客的

在删除之后还要重定向到博客列表页

服务端代码:

主要的思路就是看看博客作者id和登陆者id是不是一样的,要是一样的,就有权限去删除博客

但是在此之前,还有很多要考虑的

  1. 当前用户是否已经登录?(这个问题是要考虑的,前面已经解释过)
  2. 客户端传过来的blogId是否可能是null(前端没有传过来blogId)
  3. 传过来的blogId是否可能不存在(客户端传了blogId,但是在服务端这边根本就没有)

在经历了上面的错误情况之后,就是客户端传来正确的blogId,服务端也有对应的博客作者的id,此时只要对比一下登录用户id和博客作者id即可

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("/blog_delete")
public class BlogDeleteServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//1.先判定用户的登录状态HttpSession session = req.getSession(false);if (session == null) {resp.setStatus(403);resp.setContentType("text/html;charset=utf8");resp.getWriter().write("您当前未登录,不能删除博客!");return;}User user = (User) session.getAttribute("user");if (user == null) {resp.setStatus(403);resp.setContentType("text/html;charset=utf8");resp.getWriter().write("您当前未登录,不能删除博客 !");return;}//能走到这一步,说明已经是登录状态了//2.获取到blogIdString blogId = req.getParameter("blogId");if (blogId == null) {//说明这个blogId为空,这种情况是前端没传blogIdresp.setStatus(403);resp.setContentType("text/html;charset=utf8");resp.getWriter().write("您当前要删除的blogId有误!");return;}//3.查询这个blogId对应的blog作者BlogDao blogDao = new BlogDao();Blog blog = blogDao.selectOne(Integer.parseInt(blogId));if (blog == null) {//此时是存在一个blogId,但是它并没有对应的文章,这种情况是前端传了blogId,但是后端没有对应的数据resp.setStatus(404);resp.setContentType("text/html;charset=utf8");resp.getWriter().write("您当前要删除的博客不存在! blogId = " + blogId);return;}//观察上面的四个if可以感受到,这就是在排错,先将错误的情况都写出来,起到过滤的作用.//4.判断登录用户是不是文章作者if (blog.getUserId() != user.getUserId()) {//blog.getUserId是当前文章的作者//user.getUserId是从session中拿的登录用户信息//要是不一样,直接返回403,并且提示resp.setStatus(403);resp.setContentType("text/html;charset=utf8");resp.getWriter().write("您不能删除别人的博客!");return;}//5.真正执行删除操作//将String类型的blogId转换成int类型的,准备删除blogDao.delete(Integer.parseInt(blogId));//6.返回302,重定向到博客列表页,要是失败就提示失败resp.sendRedirect("blog_list.html");}
}

客户端核心代码:

 function upDateDeleteURL(){// 这里的location.search就是'?blogId=?'//可以在网页开发者工具的console中输入查看let deleteBtn = document.querySelector('#delete-btn');deleteBtn.href = 'blog_delete' + location.search;}upDateDeleteURL();

在Linux上部署博客系统

在完成了博客系统之后,只是可以在自己的电脑上查看博客系统, 别人还是看不到的,所以要借助公网ip来让所有人都能访问博客系统

要是想要部署java web程序,首先要配置环境

jdk tomcat mysql

安装jdk

推荐的方法是使用yum直接安装openjdk(开源的,与官方的jdk功能差不多),目前使用的最多的就是jdk8系列

yum list | grep jdk

在源上搜索所有关于jdk的文件

image-20230224200710035

devel表示development的意思,就是软件开发包

后面的x86_64的意思是支持64位的系统

选中之后按ctrl + insert来复制,之后使用yum install 粘贴(shift + insert ) 就下载好了

安装tomcat

当前的程序使用的是tomcat8.5的版本,但是使用yum list | grep tomcat 并没有8.5版本(当前的centos7系统有点老)

此时有一个好方法,找到之前的tomcat压缩包,直接拖到Linux上就能安装,主要是因为tomcat是java写的,能跨平台

注意: 在拖压缩包之前,要确认压缩包是不是zip格式的,因为Linux默认不支持rar格式的压缩包,只支持zip格式

可能会出现拖不过去的情况,此时就要安装一个lrzsz, yum insatll lrzsz

之后就能将tomcat的压缩包拖过去了

image-20230224203550512

之后进行解压,需要使用unzip ,所以要先安装unzip, yum insatll unzip

之后进行解压 unzip apache apache-tomcat-8.5.85.zip(后面的文件可以用TAB来补全)

进入到tomcat的bin目录下,就可以看到启动的脚本

image-20230224204609258

在Windows上使用的是startup.bat,但是在Linux上使用的是startup.sh

要想启动这里的程序,首先要赋予他们可执行权限,也就是chmod + x *.sh, 之后后面的文件就会变成绿色,表示可以执行

image-20230224204902904

image-20230224205155923

启动tomcat的过程:

  1. 下载安装包(要是zip格式的压缩包)
  2. 上传压缩包到Linux上(要使用到 lszrz命令)
  3. 解压缩(要使用到unzip 命令)
  4. 进入Tomcat目录的bin目录
  5. 给启动脚本增加可执行权限(chmod +x *.sh) --此时.sh的文件就会变成绿色
  6. 使用 sh startup.sh来启动 tomcat

验证tomcat是否启动成功

  • 通过ps aux | grep tomcat来查看进程

  • 通过nststat -anp | grep 8080 查看端口号是否被绑定

image-20230224210104965

image-20230224210244989

tomcat也是用java写的,所以此时8080已经被tomcat绑定了

还有一种验证tomcat是否启动的方式,就是直接访问tomcat的欢迎页面 公网IP地址:8080

但是大概率是不能访问的,这是因为云服务器的防火墙或者安全组没有对8080允许访问,所以要去云服务器那边手动添加一个8080端口号的规则

image-20230224212411896

添加完8080端口号的规则之后就能正常访问tomcat的欢迎页面

image-20230224212505240

但是在平时的时候还是不要开启端口号规则,防止服务器被攻击

安装mysql

linux安装mysql有很多的方式,最简单的一种安装方式是使用yum 安装mariadb(开源的,是mysql的孪生兄弟)

使用yum安装必要的包

直接执行这几个命令就行了

yum install -y mariadb-server
yum install -y mariadb
yum install -y mariadb-libs
yum install -y mariadb-devel

启动

启动mariadb服务

systemctl start mariadb

设置服务自启动

systemctl enable mariadb

查看服务的状态

systemctl status mariadb

验证是否连接上

mysql -uroot

image-20230224214033912

这样子就是连接成功了

创建数据库的时候为了支持中文,这里统一使用utf8mb4字符集

create database demo_db charset utf8mb4;

此时就将mariadb安装连接好了

正式部署

在安装好了jdk tomcat mysql之后,就可以开始将博客系统部署到云服务器上

  1. 首先要理清楚 博客系统的依赖,先将依赖的内容打通

  2. 将博客系统的程序打包, 将war包上传到云服务器的webapps目录下即可

    在我的博客系统中的依赖就是mysql,必须要知道的是,本地的电脑上的数据库与云服务器上的数据库是两台不一样的电脑,所以本地的数据库有数据,但是云服务器上的数据库还没有数据

    所以此时要将SQL指令在云服务器上输入(粘贴)一下

进入mariadb 的命令: mysql -uroot,退出mariadb: ctrl + c

在开始打war包之前要先调整一下DBUtil的代码,主要就是要调整一下连接数据库的代码

image-20230225102637117

需要将这里的密码设置成云服务器的数据库的密码,要是没有单独设置过云服务器上的密码,那么就是空字符串

image-20230225102959779

这里的setURL也基本上不用改,因为云服务器也是在我主机上的,所以还是可以使用环回IP, 后面的3306端口号也是不用改的

打war包的时候

image-20230225101928966

改好之后双击maven的package就好了

image-20230225103327293

打好war包之后找到war包

在Linux上进入到apache tomcat目录中,在进入webapps目录中

image-20230225103709894

将之前打好的war包拖到webapp下面

image-20230225104002387

之后就会自动进行解压缩和部署,变成蓝色的了

要是之后修改了代码,务必要重新打包,重新上传

部署(也叫上线)是一个很重要,很有仪式感的事情,所以一定要小心谨慎!

此时就已经是部署完毕了!

注意: 要是发现网页打不卡,先检测一下tomcat的欢迎页面能不能打开,要是不能打开说明是tomcat8080端口被阻止了,就要去云服务器那边将防火墙新增8080端口的规则

要知道本地电脑上能运行,不能说明部署到云服务器上就能成功

此时我出现了一个问题,那就是mariadb上的中文乱码,但是我在建库的时候已经指定了utf8mb4字符集,最后我发现还要在建表的时候也要指定字符集

image-20230225114035918

要是修改了任何代码都要重新使用maven双击package重新打war包

再把之前Linux上的war删除,再把新的war包拖进去,就能重新部署

此时,就正式完成了博客系统的简单功能实现和部署

博客系统的源码(服务端+客户端)

相关文章:

博客系统(前后端分离版)

博客系统的具体实现 文章目录博客系统的具体实现软件开发的基本流程具体实现的八大功能数据库设计创建数据库操作数据库引入依赖封装DataSource创建实体类将JDBC增删改查封装起来实现博客列表页web.xml的配置文件实现博客系统的展示功能登录功能强制要求用户登录显示用户信息退…...

第十二章 opengl之模型加载(Assimp)

OpenGLAssimp模型加载库构建Assimp网格网格渲染Assimp 我们不太能够对像是房子、汽车或者人形角色这样的复杂形状手工定义所有的顶点、法线和纹理坐标。我们要的是将这些模型(Model)导入(Import)到程序当中。模型通常都由3D艺术家在Blender、3DS Max或者Maya这样的工具中精心制…...

Stable Matching-稳定匹配问题【G-S算法,c++】

Stable Matching-稳定匹配问题【G-S算法&#xff0c;c】题目描述&#xff1a;(Gale-Shapley算法)解题思路一&#xff1a;G-S算法(Gale-Shapley算法)题目描述&#xff1a;(Gale-Shapley算法) Teenagers from the local high school have asked you to help them with the organ…...

TypeScript(四)接口

目录 前言 定义 用法 基本用法 约定规则 属性控制 任意属性 可选属性 只读属性 定义函数 冒号定义 箭头定义 接口类型 函数接口 索引接口 继承接口 类接口 总结 前言 在介绍TS对象类型中&#xff0c;为了让数组每一项更具体&#xff0c;我们使用 string [ ]…...

Python-基础知识

目录 Python 简介 Python 发展历史 Python 特点 Python 标识符 Python 保留字符 行和缩进 多行语句 Python 引号 Python注释 Python 简介 Python 是一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言。 Python 的设计具有很强的可读性&#xff0c;相比…...

【java基础】集合基础说明

文章目录基本介绍Collection接口Iterator和Iterable接口Map接口关于Iterator接口的一些说明框架中的接口具体集合总结基本介绍 集合就是存储用来存储一系列数据的一种数据结构。在这篇文章中会介绍集合的一些基本概念。 Collection接口 集合的基本接口是Collection接口&…...

MySQL的下载及安装详细教程

提示&#xff1a;本文仅为MySQL初学者的安装MySQL过程提供参考&#xff0c;创作不易&#xff0c;请多点赞支持&#xff01; MySQL的下载及安装前言一、MySQL的下载及安装1.MySQL的下载2.MySQL的安装3.配置环境变量4.连接MySQL4.1 方式一4.2 方式二前言 本文内容主要是帮助初学…...

SSL/TLS协议工作原理

SSL/TLS协议工作原理 SLL/TLS协议工作在应用层和传输层之间&#xff0c;应用层数据需要经过SSL/TLS层的加密之后才会发送到传输层。SSL/TLS协议有两个重要协议&#xff1a;握手协议、记录协议。 1. 握手协议 TCP三次握手完成后&#xff0c;才能进行SSL/TLS的握手。 因为&#…...

大数据项目实战之数据仓库:用户行为采集平台——第4章 用户行为数据采集模块

第4章 用户行为数据采集模块 4.1 数据通道 4.2 环境准备 4.2.1 集群所有进程查看脚本 1&#xff09;在/home/atguigu/bin目录下创建脚本xcall [atguiguhadoop102 bin]$ vim xcall2&#xff09;在脚本中编写如下内容 #! /bin/bashfor i in hadoop102 hadoop103 hadoop104 d…...

《统计学习方法》(李航)——学习笔记

第一章 概论统计学习&#xff0c;又称统计机器学习&#xff08;机器学习&#xff09;&#xff0c;现在提到的 机器学习 往往指的就是 统计机器学习。统计学习研究的对象是数据&#xff0c;其对数据的基本假设是同类数据存在一定的统计规律性&#xff0c;因此可以用概率统计方法…...

阿里云EMR集群搭建及使用

目录 1.简介 1.什么是EMR 2.组成 3.与自建hadoop集群对比 4.产品架构 2.使用 1.创建EMR集群 1.登录EMR on ECS控制台 2.软件设置 3.硬件设置 3.基础配置 2.配置 1.组件配置 2.用户管理 3.安全组 4.Gateway 3.组件UI 1.简介 1.什么是EMR EMR是运行在阿里云平台…...

学习streamlit-4

st.slider 今天学习st.slider滑块组件的使用。 st.slider滑块组件通常被用来作为应用的输入&#xff0c;支持整数、浮点数、日期、时间和日期时间。 下面的示例程序包含以下简单功能&#xff0c;以演示st.slider滑块组件&#xff1a; 用户通过调整滑块选择值应用打印出所选…...

高级Oracle DBA面试题及答案

作为高级 Oracle DBA&#xff0c;您将负责 Oracle 数据库基础架构的设计、安装、配置、监控和维护。您还将负责制定和实施备份和恢复计划&#xff0c;并确保数据的安全性和完整性。要成功担任此职位&#xff0c;您需要对 Oracle 数据库架构有深入的了解&#xff0c;并能够有效地…...

程序员成长路线

程序员在成长的过程中&#xff0c;不同的阶段&#xff0c;需要关注的问题点一会都会有所不同&#xff0c;今天给大家分享下自己的感受。 0-1年&#xff0c;入门&#xff0c;掌握语言基础、提高工具的使用熟练度。 工作第一年&#xff0c;主要围绕ssm三件套、mysql、red…...

【Galois工具开发之路】关于类的重新装载思路

思路 当一个java的类文件发生变更&#xff0c;如果动态的热更新这个新的类文件&#xff1f;目前来说&#xff0c;有两种可能的方式 新增一个自定义ClassLoader&#xff0c;名为NC&#xff0c;让NC去load这个新的类文件&#xff0c;这样就完成了新的类定义的替换 但目前Java有…...

哪款蓝牙耳机音质好?内行推荐四款高音质蓝牙耳机

蓝牙耳机经过近几年的快速发展&#xff0c;在音质上的表现也越来越好。哪款蓝牙耳机音质好&#xff1f;最近看到很多人问。接下来&#xff0c;我来给大家推荐四款高音质蓝牙耳机&#xff0c;可以当个参考。 一、南卡小音舱蓝牙耳机 参考价&#xff1a;246 发声单元&#xff…...

Android程序自动在线升级安装

安卓小白分享: Android程序自动在线升级安装.(通过GetSharedDownloadsPath方法) 1>.修改AndroidManifest.template.xml ( 此文件在你DELPHI项目的目录中,如找不到就文件查找吧) 最好把此文件拖到DELPHI, 用DELPHI打开,(这样,它会一行一行格式清楚) 找到文字<%u…...

JS的BroadcastChannel与MessageChannel

BroadcastChannel与MessageChannel BroadcastChannel BroadcastChannel以广播的形式进行通信 BroadcastChannel用于创建浏览器标签页之间的通信 使用BroadcastChannel的浏览器标签页面必须要遵循同源策略 页面1使用BroadcastChannel创建一个频道&#xff0c;页面2使用Broadc…...

nextjs开发 + vercel 部署 ssr ssg

前言 最近想实践下ssr 就打算用nextjs 做一个人博客 &#xff0c; vercel 部署 提供免费域名&#xff0c;来学习实践下ssr ssg nextjs 一个轻量级的react服务端渲染框架 vercel 由 Next.js 的创建者制作 支持nextjs 部署 免费静态网站托管 初始化项目 npx create-next-app p…...

Good Idea, 利用MySQL JSON特性优化千万级文库表

&#x1f473;我亲爱的各位大佬们好&#x1f618;&#x1f618;&#x1f618; ♨️本篇文章记录的为 利用MySQL JSON特性优化千万级文库表 相关内容&#xff0c;适合在学Java的小白,帮助新手快速上手,也适合复习中&#xff0c;面试中的大佬&#x1f649;&#x1f649;&#x1f…...

【python游戏制作】快来跟愤怒的小鸟一起攻击肥猪们的堡垒吧

前言 嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! 为了防止/报复偷走鸟蛋的肥猪们&#xff0c;鸟儿以自己的身体为武器&#xff0c; 仿佛炮弹一样去攻击肥猪们的堡垒&#xff0c;保卫自己的鸟蛋 这个游戏大家没玩过的想必也听说过~ 今天就给大家分享一下用python写的愤怒的…...

ARM 学习(一)

ARM 处理器的运行模式ARM处理器共有7种运行模式&#xff0c;如下表所示&#xff1a;处理器模式描述用户模式&#xff08;User&#xff09;正常程序运行模式中断模式&#xff08;IRQ&#xff09;用于通常的中断处理快速中断模式&#xff08;FIQ&#xff09;用于高速传输和通道处…...

深入分析Java的序列化与反序列化

序列化是一种对象持久化的手段。普遍应用在网络传输、RMI等场景中。本文通过分析ArrayList的序列化来介绍Java序列化的相关内容。主要涉及到以下几个问题&#xff1a; 怎么实现Java的序列化 为什么实现了java.io.Serializable接口才能被序列化 transient的作用是什么 怎么自…...

、Tomcat源码分析-类加载器

接下来&#xff0c;我们再来看下 tomcat 是如何创建 common 类加载器的。关键代码如下所示&#xff0c;在创建类加载器时&#xff0c;会读取相关的路径配置&#xff0c;并把路径封装成 Repository 对象&#xff0c;然后交给 ClassLoaderFactory 创建类加载器。 Bootstrap.java…...

反转链表相关的练习(下)

目录 一、回文链表 二、 重排链表 三、旋转链表 一、回文链表 给你一个单链表的头节点 head &#xff0c;请你判断该链表是否为回文链表。如果是&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,2,1] 输…...

2.进程和线程

1.进程1.1 终止正常退出(自愿)出错退出(自愿)严重错误(非自愿)被其他进程杀死(非自愿)1.2 状态就绪态&#xff1a;可运行&#xff0c;但因为其他进程正在运行而暂时停止阻塞态&#xff1a;除非某种外部事件发生&#xff0c;否则进程不能运行1.3 实现一个进程在执行过程中可能被…...

C++回顾(十四)—— 函数模板

14.1 概述 所谓函数模板(function template)&#xff0c;实际上是建立一个通用函数&#xff0c;其函数类型和形参类型不具体指定&#xff0c;用一个虚拟的类型来代表。这个通用函数就称为函数模板。凡是函数体相同的函数都可以用这个模板来代替&#xff0c;不必定义多个函数&a…...

如何做好项目各干系人的管理及应对?

如何更好地识别、分析和管理项目关系人&#xff1f;主要有以下几个方面&#xff1a; 1、项目干系人的分析 一般对项目干系人的分析有2种方法&#xff0c; 方法一&#xff1a;权利&#xff08;影响&#xff09;&#xff0c;即对项目可以产生影响的人&#xff1b; 方法二&#xf…...

Elasticsearch使用系列-ES增删查改基本操作+ik分词

一、安装可视化工具KibanaES是一个NoSql数据库应用。和其他数据库一样&#xff0c;我们为了方便操作查看它&#xff0c;需要安装一个可视化工具 Kibana。官网&#xff1a;https://www.elastic.co/cn/downloads/kibana和前面安装ES一样&#xff0c;选中对应的环境下载&#xff0…...

07-PL/SQL基础(if语句,case语句,循环语句)

本章主要内容&#xff1a; 1.PL/SQL的基本构成&#xff1a;declare,begin,exception,end; 2.结构控制语句:IF语句,CASE语句 3.循环结构&#xff1a;loop循环&#xff0c;for loop循环&#xff0c;while loop循环 PL/SQL的基本构成 特点 PL/SQL语言是SQL语言的扩展&#xff…...