博客系统实现

一.准备工作

1.创建项目,把前端写好的博客静态页面拷贝到webapp目录中

2.引入依赖,这里主要用到servlet,mysql5.1.47,jacson2.15.0

3.找到右上角的edit configurations->smartTomcat->进行配置

4.数据库设计:设计对应的表结构,并把数据库相关代码进行封装。如何设计表结构?首先确定实体,其次确认实体之间的关系。这里的实体就是博客和用户,所以就是一个blog表,一个user表。两者是一对多的关系->一个博客只能属于一个用户,然而一个用户可以拥有多篇博客。所以应在博客表中引入userid。

1.对数据库的操作

先把数据库创建好,并创建好表,为了方便创建,先在webapp下面新建一个文件db.sql,然后再这里面把sql语句编辑好,然后复制粘贴到MySQL中。这样方便修改语法错误(其实直接在MySQL中创建也可以)

然后我们把对数据库进行操作的代码进行封装。我们将对数据库的操作都放到model包中。所以在java目录下新建一个model包

建立连接等通用操作

然后再在model包中新建一个DButil类,用来封装数据库建立连接的代码。这是由于接下来的代码中有很多个servlet都需要使用库,所以就需要有单独的地方把DataSourse的操作进行封装,而不是只放到某个servlet的init中

public class DButil {private static DataSource ds=null;private static DataSource getDataSourse(){if(ds==null){ds=new MysqlDataSource();((MysqlDataSource)ds).setUrl("jdbc:mysql//127.0.0.1:3306/blog_system?charset=utf8&useSSL=false");((MysqlDataSource)ds).setUser("root");((MysqlDataSource)ds).setPassword("20050430zyh");}return ds;}public Connection getConnection() throws SQLException {return getDataSourse().getConnection();}public static void close(ResultSet resultSet, PreparedStatement statement,Connection connection){if(resultSet!=null){try {resultSet.close();} catch (SQLException e) {e.printStackTrace();}}if(statement!=null){try {statement.close();} catch (SQLException e) {e.printStackTrace();}}if(connection!=null){try {connection.close();} catch (SQLException e) {e.printStackTrace();}}}
}

如上,对外只开放建立连接和关闭的操作,建立数据源的操作直接封装起来

两个类表示两张表

每个表都需要专门搞一个类来表示

public class Blog {private int blogid;private String content;private Timestamp postTime;private int userid;public int getBlogid() {return blogid;}public void setBlogid(int blogid) {this.blogid = blogid;}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;}@Overridepublic String toString() {return "Blog{" +"blogid=" + blogid +", content='" + content + '\'' +", postTime=" + postTime +", 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;}@Overridepublic String toString() {return "User{" +"userid=" + userid +", username='" + username + '\'' +'}';}
}

两个类完成对两张表的操作

就用BlogDao和UserDao来命名。Dao,就是Data Access Object(数据访问对象)通过这两个类的对象完成对数据的操作

public class BlogDao {//新增,提交博客public void insert(Blog blog){Connection connection=null;PreparedStatement statement=null;try {//建立连接connection=DButil.getConnection();//构造sql语句String sql="insert into blog values (null,?,?,now(),?)";statement=connection.prepareStatement(sql);statement.setString(1,blog.getTitle());statement.setString(2,blog.getContent());statement.setString(3,""+blog.getUserid());//执行sqlstatement.executeUpdate();} catch (SQLException e) {e.printStackTrace();}finally {DButil.close(null,statement,connection);}}//查询博客列表(博客列表页),把库中所有博客都拿到public List<Blog> getBlogs(){Connection connection=null;PreparedStatement statement=null;ResultSet resultSet=null;List<Blog> blogList=new ArrayList<>();try {connection=DButil.getConnection();String sql="select * from blog";statement=connection.prepareStatement(sql);resultSet=statement.executeQuery();while(resultSet.next()){Blog blog=new Blog();blog.setBlogid(resultSet.getInt("blogid"));blog.setTitle(resultSet.getString("title"));String content=resultSet.getString("content");if(content.length()>100){content=content.substring(100);}blog.setContent(content);blog.setPostTime(resultSet.getTimestamp("postTime"));blog.setUserid(resultSet.getInt("userid"));blogList.add(blog);}} catch (SQLException e) {e.printStackTrace();}finally {DButil.close(resultSet,statement,connection);}return blogList;}//根据博客id查询指定博客public Blog getBlog(int blogid){Connection connection=null;PreparedStatement statement=null;ResultSet resultSet=null;Blog blog=new Blog();try {connection=DButil.getConnection();String sql="select * from blog where blogid=?";statement=connection.prepareStatement(sql);statement.setInt(1,blogid);resultSet=statement.executeQuery();if(resultSet.next()){blog.setBlogid(resultSet.getInt("blogid"));blog.setTitle(resultSet.getString("title"));blog.setContent(resultSet.getString("content"));blog.setPostTime(resultSet.getTimestamp("postTime"));blog.setUserid(resultSet.getInt("userid"));}} catch (SQLException e) {e.printStackTrace();}finally {DButil.close(resultSet,statement,connection);}return blog;}//根据博客id删除指定博客public void deleteBlog(int blogid){Connection connection=null;PreparedStatement statement=null;try {connection=DButil.getConnection();String sql="delete from blog where blogid=?";statement=connection.prepareStatement(sql);statement.setInt(1,blogid);statement.executeUpdate();} catch (SQLException e) {e.printStackTrace();}finally {DButil.close(null,statement,connection);}}
}

注意,在getBlogs这个方法中,获取正文时,不要把全部正文内容都拿出来显示到列表页上,只需要拿出一部分即可,所以会有一个截断操作

在getBlog操作中,由于blogid时主键,所以只能查询到一篇博客,所以不需要while循环,只需要一个if判断,同时,正文不用截断!!!

public class UserDao {//根据userid查询对应用户信息(获取用户信息)public User getUserByid(int userid){Connection connection=null;PreparedStatement statement=null;ResultSet resultSet=null;User user=new User();try {connection=DButil.getConnection();String sql="select * from user where userid=?";statement=connection.prepareStatement(sql);statement.setInt(1,userid);resultSet=statement.executeQuery();if(resultSet.next()){user.setUserid(resultSet.getInt("userid"));user.setUsername(resultSet.getString("usernsme"));user.setPassword(resultSet.getString("password"));}} catch (SQLException e) {e.printStackTrace();}finally {DButil.close(resultSet,statement,connection);}return user;}//根据usernsme查询对应用户信息public User getUserByname(String username){Connection connection=null;PreparedStatement statement=null;ResultSet resultSet=null;User user=new User();try {connection=DButil.getConnection();String sql="select * from user where userid=?";statement=connection.prepareStatement(sql);statement.setString(1,username);resultSet=statement.executeQuery();if(resultSet.next()){user.setUserid(resultSet.getInt("userid"));user.setUsername(resultSet.getString("usernsme"));user.setPassword(resultSet.getString("password"));}} catch (SQLException e) {e.printStackTrace();}finally {DButil.close(resultSet,statement,connection);}return user;}
}

上面这两方法就都很相似。

二.前后端交互,实现博客功能

1.获取博客列表页

在博客列表页加载时,通过ajax方式给服务器发送请求,从服务器数据库中拿到数据显示到页面上

约定前后端交互接口

请求

GET /blog

响应

HTTP/1.1 200 OK

Content-Type:application/json

[

     {

         blogid:1

         title:' '

         content:

         postTime:

         userid:

      }

……

]

让浏览器给服务器发送请求

我们要对前端的blog_list.html使用ajax进行修改,所以首先要引入jquery库:也就是在head标签内加一个script标签:<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.min.js"></script>

编写前把写死的那部分标签给注释掉,然后在</body>上方进行代码编写,加一个<script>标签,在开始和结束标签之间进行编写:

我们直接将代码封装到函数中,但一定要记住调用!!

服务器处理请求

我们将所有服务器的代码都放到一个包里面,也就是servlet包,然后在改包中线新建一个BlogServlet类。由于对于博客列表页发送的请求的处理方式是要将表中数据按照json格式字符串返回,所以肯定要用到jackson,所以要定义一个成员变量ObjectMapper,最终代码如下:

让前端代码处理响应

这里的关键是根据服务器返回的博客信息去构造博客列表页!!我们使用的是比较朴素的方式来构造博客列表页,也就是基于dom api。dom api是浏览器提供的标准的api,不属于任何第三方库/框架。

如上,我们要构建出类似于这样结构的标签,这些标签有着包含关系以及并列关系,最大的标签是containner-right,其他所有标签都是在它里面。它里面就是一个一个博客,其中每个博客标签里面又并列包含了标题,发布时间,摘要,查看全文按钮

如上。由于在像一个中设置了正文格式为json,所以当把响应返回给客户端后,body部分就自动被解析成了一个json对象,这里由于响应是一个对象列表转化成的json字符串数组,所以是被解析成了json对象数组,所以要通过for循环遍历数组,拿出每一个blog对象进行遍历。

其中,在创建查看全文按钮时,我们的目的是创建一个类似于”查看全文>>"这样的标签,但是大于号要想在html中写出,就必须使用转义字符&gt;小于号就是&lt。因为html标签就是<>构成的。

其次,一点击查看全文,就要进入博客详情页,所以要用到超链接标签,也就是a标签。其中的href属性就是在描述点击后会跳转到哪个页面。同时,点击a标签就会自动发送一个get请求!!!之前我们提到过。

解决出现的问题

到这里,博客列表页就构造完了。我们来测试一下,发现几个问题:1.时间显示不对,现在显示的是时间戳,但我们希望是某年某月某日几点。2.返回的数据的顺序问题,正常来说,最上面的博客应该是最新的博客,但是我们现在再插入一篇博客后,它会被显示到博客列表页的最下面,而不是最上面。

解决时间问题

抓包观察是哪里出现了问题,仔细观察发现在获取博客数据的响应中的postTime就是时间戳。所以是resp.getWriter().write(respJson)出问题了。所以关键就是respJson,它是怎么获取到的呢?

1.首先jackson发现,blogList是一个list,于是就循环遍历了

2.它针对每一个元素(Blog对象)通过反射的方式获取到属性名字,然后再用get方法拿到属性的值。

所以关键就在于修改postTime的get方法,如下:

java标准库中提供了SimpleDateFormet类,来完成时间戳到格式化时间的转化。此类的使用很复杂,不需要背,每次使用前都查一下就行。创建SDF对象,传入指定字符串,用来描述当前时间日期的具体格式,然后使用format方法,里面的参数可以是时间戳,也可以是date对象

解决顺序问题

我们是到,数据库查询到结果的顺序其实是随机的,所以要想固定顺序,就要使用orderby关键字,并按照发布时间降序排列,如下选中部分。

2.获取博客详情页

这个就是在点击查看全文后,就会发送get请求并跳转到了博客详情页(这是我们刚刚编写的a标签实现的逻辑),那我们现在要实现的就是根据请求中的blogid去查询到对应的博客并返回给前端

约定前后端交互接口

请求

GET /blog?blogid=1

响应

HTTP/1.1 200 OK

Content-Type:application/json

{

      blogid:1

      title:

      content:

      postTime:

      userid:

}

前端使用ajax发起请求

打开blog_list.html,在</div>下面添加script标签,进行代码编写:

url中的blogid是如何得到的呢?这里就是用location.search来拿到当前页面的url中的queryString(因为在设置a标签时,其中的href属性中就带有?blogid=……)。(在浏览器中,ctrl+shift+i就可以打开控制台,输入location.search就可以看到当前页面的queryString语句)注意,location.search拿到的是整个query String语句,包括了问号!!

服务器处理上述请求

上面请求的路径还是blog,所以还是在BlogServlet中处理get方法。但是在获取博客列表页的时候已经写了一个doGet方法了呀,这该怎么办?找两者的不同,发现上面的请求时没有blogid的,而当前这个请求是有blogid的。这就是区别。所以进行如下编辑:

前端将响应数据构造成html片段

首先把containner-right里面的内容给注释掉

然后按这个格式去构造片段:

测试一下,发现了两个问题:

1.写完代码之后,会发现点击某个博客,有点博客详情页里还是那些注释掉的内容。这个问题是浏览器缓存引起的。浏览器在加载页面时,是通过网络获取资源的,但是网络速度很慢,所以浏览器会把已经加载是页面在本地硬盘中保存一份,后续再次访问同一个页面时,就不通过网络加载,而是直接加载本地硬盘中的这一份。那这如何克服呢?前端有专业的解决方案,不过咱们不用关心,只需要ctrl+F5刷新一下即可。

当前的详情页,虽然能够显示正文了,但是显示的正文是markdown的原始数据。正常应该显示md渲染后的效果。这就需要通过引入第三方库来完成:

引入依赖:在blog_editor.html的</head>前面粘贴

然后在刚才的构造页面正文的代码进行修改:

这个editormd是editor.md官方文档上提供的一个全局变量。此方法的第一个参数必须是一个标签的id,但是content标签没有设置id,所以我们要在上面的content标签内部加上一个id属性。第二个参数就是一个js对象:

最终这个函数的效果就是把blog.content这里的原始md数据渲染成html放到content div的内容中

3.实现登录

在login.html点击登陆后,应该给服务器发起一个http请求。服务器处理上述请求,读取用户名和密码,在库中查询匹配,若正确,则成功登录,创建会话,跳转到博客列表页(所以登陆成功就直接进行重定向跳转

约定前后端交互接口

请求

POST /login

Content-Type:application/x-www-form-urlencoded

username= &password=

响应

HTTP/1.1 302

Location:blog_list.html

注意,若使用ajax发送请求,就要再写代码完成跳转;但是使用form表单的话,只要提交成功,就可以直接使用302完成页面跳转

前端发送请求

打开login.html,对下面的代码进行逻辑修改:

修改完就是:

服务器处理请求返回响应

先将请求中的用户名和密码获取到,判断是否为空。然后根据用户名去获取对应的用户,要是没获取到,就说明用户名错误了,若获取到,就说明用户名是对的。然后拿着刚刚获取到的用户的密码和请求中的密码对照一下,要是不一致,就说明密码填错了(不过,不管是密码写错买时用户名写错,我们都统一提示用户名或密码不正确)。最后创建会话,还是参数是true,并且设置会话中的属性user就是user对象。

4.强制要求登录

这个功能就是在博客的列表页,详情页,编辑页去判定当前用户是否已经登陆。若未登录,就强制要求跳转到登录页。所以要在这几个页面中,在页面加载时,给服务器发送ajax请求,从服务器获取到一个登录状态即可

约定前后端交互接口

请求

GET /login

响应

HTTP/1.1 200 OK ->表示成功登录

HTTP/1.1 403       ->表示登录失败

响应的格式可以有很多种,比如返回的都是200,但是正文不一样

前端发起ajax请求

打开blog_list.html,在function getBlogs()下面再写一个方法:

这里success是在返回2开头的状态码时会执行,error则是在返回除2以外的数字开头的状态码就hi执行。location.assign就是强制要求跳转到login.html,这是前端页面跳转的方式。那为啥不待会儿在服务器直接返回个302,直接跳转到登录页呢?因为302这样的响应回到ajax中的error中后,无法被ajax直接处理。除非通过提交form表单或者点击a标签这种触发的http请求,浏览器才可以直接跳转。

处理响应的过程比较简单,所以一起写了

服务器处理请求

注意,未登录状态的判定不单单是看会话是否存在,还有看该会话中是否存放着user(为什么?待会实现退出登录的时候就知道了)

测试一下:直接打开列表页,在列表页刷新一下,就发现跳转到了登录页。

但有个问题,虽然已经登陆了,但是一旦重启服务器,就会被判定为未登录状态。因为登陆状态是根据服务器的内存中的session确定的,重启服务器意味着之前的内存要释放。其实这一设定并不科学,如何解决?以后再说。

现在,博客编辑页和详情页也要执行上述逻辑,所以在vscode中创建一个新目录js,js中创建新文件app.js,将getLoginStqatus函数复制过去。

然后在每个页面的<script>上再来个<script src="js/app.js"></script>,然后在下面的script中调用getLoginStatus即可:

这就是把一些公共的js代码单独提取出来放到某个.js文件中,然后通过html中的script标签来引用这些文件内容,此时就可以在html中调用对应的公共代码了。

5.显示用户信息

在列表页显示当前登录的用户的信息,在详情页显示作者信息。在页面加载时,给服务器发送ajax请求,服务器返回对应的用户信息

约定前后端交互接口

请求

GET /userinfo(详情页则是authorid)

响应

HTTP/1.1 200 OK

Type:application/json

{

        userid:

        username:

}

前端发起ajax请求

这是博客列表页

这是博客详情页,通过location.search查到对应的博客id,从而查找对应的作者

服务器处理上述请求

首先是针对博客列表页的。最终要将user对象转成json格式的字符串,所以要有一个ObjectMapper。然后,先取出会话,看看会话是否为空,或者会话中是否存储了user,要是没有,就说明未登录,反之就是已经登录了。最后,在显示用户信息时,为保证安全,就应该将密码置为空!!

最后就是处理博客详情页的请求,首先看看请求中是否带有blogid,要不然没法查询。然后就是根据blogid查到对应的博客。再根据博客找到作者,最后返回作者信息

前端将响应构造成html片段

首先是列表页:

应该将用户名显示到<h3>标签处,所以代码如下:

注意,h3标签没有class属性,所以前面不用加点。

然后是详情页,也是要设置到h3标签:

6.注销/退出登录

在博客列表页/编辑页/详情页都有注销按钮,这就是一个a标签,其中有一个href属性,点击就能够触发一个http请求,就能引起浏览器跳转到另一个页面。

我们现在要做的就是修改a标签,让用户一点击它,就能触发一个http请求,服务器收到了就会把会话中的这个user删除掉并跳转到登录页。(那为啥不直接把会话给删除呢?因为servlet没有提供删除会话的操作!!!不过也可以给会话设置一个过期时间,但是不够优雅。session提供了removeAttribute的方法)

约定前后端交互接口

请求

GET /logout

响应

HTTP/1.1 302

location:login.html

前端发起请求

不用写ajax,直接给a标签添加一个href属性即可

服务器处理请求

7.发布博客

输入标题,正文,期望一点击提交就能够发到服务器这边进行保存

约定前后端交互接口

请求

POST /blog

content-Type:application/x-www-form-urlencoded

title= &content=

响应

HTTP/1.1 302

location:blog_list.html

前端发起请求

就是修改这一块,变成:

也就是给title标签加上name属性。那么如何给正文加上name属性?就是在 <div id="editor">这个标签下加上<textarea name="content" style="display:none"></textarea>,textarea就是一个多行编辑器容器,就把name属性加到它上面即可,然后在初始化editormd对象时加上一个对应的属性即可,也就是在下面的图片中的var editor 中加一个属性saveHTMLToTextarea并赋值为true:

上面这个图片修改为:

表示会把用户在编辑器里面的内容自动保存到textarea中,这样,一点提交,就会在form表单中有拷贝

服务器处理请求

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/802733.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

吴恩达深度学习 (week1,2)

文章目录 1、神经网络监督学习2、深度学习兴起原因3、深度学习二元分类4、深度学习Logistic 回归5、Logistic 回归损失函数6、深度学习梯度下降法7、深度学习向量法8、Python 中的广播9、上述学习总结10、大作业实现:rocket::rocket:&#xff08;1&#xff09;训练初始数据&…

Matlab进阶绘图第49期—气泡堆叠图

气泡堆叠图是堆叠图与气泡图的组合—在堆叠图每根柱子上方添加大小不同的气泡&#xff0c;用于表示另外一个数据变量&#xff08;如每根柱子各组分的平均值&#xff09;的大小。 本文利用自己制作的BarBubble工具&#xff0c;进行气泡堆叠图的绘制&#xff0c;先来看一下成品效…

从数据采集到可视化展示Node-Red二次开发4G模块

环境监测正逐步迈入数字化、智能化时代。Node-Red作为一种开源流式编程工具&#xff0c;以其强大的数据处理能力和设备集成便捷性&#xff0c;在构建环境监测数据站中发挥着至关重要的作用。钡铼技术支持Node-Red编程开发&#xff0c;支持BLIoTLink软网关和自定义开发非标协议。…

为什么 MySQL 采用 B+ 树作为索引?

资料来源 : 小林coding 小林官方网站 : 小林coding (xiaolincoding.com) 「为什么 MySQL 采用 B 树作为索引&#xff1f;」这句话&#xff0c;是不是在面试时经常出现。 要解释这个问题&#xff0c;其实不单单要从数据结构的角度出发&#xff0c;还要考虑磁盘 I/O 操作次数&am…

【六 (3)机器学习-机器学习建模步骤/kaggle房价回归实战】

目录 文章导航一、确定问题和目标&#xff1a;1、业务需求分析&#xff1a;2、问题定义&#xff1a;3、目标设定&#xff1a;4、数据可行性评估&#xff1a;5、资源评估&#xff1a;6、风险评估&#xff1a; 二、数据收集&#xff1a;1、明确数据需求2、选择数据来源3、考虑数据…

SpringCloud Alibaba Seata 处理分布式事务

一、前言 接下来是开展一系列的 SpringCloud 的学习之旅&#xff0c;从传统的模块之间调用&#xff0c;一步步的升级为 SpringCloud 模块之间的调用&#xff0c;此篇文章为第十八篇&#xff0c;即使用 Seata 处理分布式事务。 二、分布式事务问题 当单体应用被拆分成微服务应用…

【超简单】基于PaddleSpeech搭建个人语音听写服务

一、【超简单】之基于PaddleSpeech搭建个人语音听写服务 1.需求分析 亲们,你们要写会议纪要嘛?亲们,你们要写会议纪要嘛?亲们,你们要写会议纪要嘛?当您面对成吨的会议录音,着急写会议纪要而不得不愚公移山、人海战术?听的头晕眼花,听的漏洞百出,听的怀疑人生,那么你…

代码随想录算法训练营Day48|LC198 打家劫舍LC213 打家劫舍IILC337 打家劫舍III

一句话总结&#xff1a;前两题白给&#xff0c;第三题树形DP有点难。 原题链接&#xff1a;198 打家劫舍 滚动数组直接秒了。 class Solution {public int rob(int[] nums) {int n nums.length;int first 0, second nums[0];for (int i 2; i < n; i) {int tmp Math.m…

如何开始用 C++ 写一个光栅化渲染器?

光栅化渲染器是计算机图形学中最基础且广泛应用的一种渲染技术&#xff0c;它将三维模型转化为二维图像。下面我们将逐步介绍如何使用C语言从零开始构建一个简单的光栅化渲染器。 一、理解光栅化渲染原理 光栅化是一种将几何数据&#xff08;如点、线、三角形&#xff09;转换…

电商选品难?那是因为你不会用大数据选品工具…

电商选品之所以难&#xff0c;主要有以下几个方面的原因。电商市场更新换代非常快&#xff0c;新的产品不断涌现&#xff0c;旧的产品可能很快就被淘汰。电商选品紧跟市场趋势&#xff0c;不断调整和更新&#xff0c;这对电商运营市场敏感度和反应速度提出了很高的要求。 电商…

110V降9V1A非隔离降压恒压WT5112

110V降9V1A非隔离降压恒压WT5112 嘿&#xff0c;让我来给你说说这个WT5112控制芯片。这可是个厉害的东西&#xff0c;特别适合用在充电器啊、适配器啊还有LED灯这些地方。它最牛的地方就是能稳稳地控制电压和电流&#xff0c;而且还有个什么原边反馈技术让控制得更准。更酷的是…

MySQL_00001_00000

数据准备 员工表&#xff1a;emp Oracle: create table emp ( empno number(4) not null, ename varchar2(10), job varchar2(9), mgr number(4), hiredate date, sal number(7, 2), comm number(7, 2), deptno number(2) ); insert into em…

数据库讲解---(SQL语句--表的使用)【MySQL版本】

零.前言 数据库讲解&#xff08;MySQL版&#xff09;&#xff08;超详细&#xff09;【第一章】-CSDN博客 数据库-ER图教程_e-r图数据库-CSDN博客 数据库讲解&#xff08;MySQL版&#xff09;&#xff08;超详细&#xff09;【第二章】【上】-CSDN博客 一.SQL概述 1.1SQL简…

组合逻辑电路中的竞争与冒险

竞争与冒险 进行理想的组合逻辑电路分析与设计时&#xff0c;没有考虑逻辑门的延迟时间&#xff08;原因&#xff09;对电路产生的影响&#xff0c;且认为电路的输入和输出均处于稳定的逻辑电平。 实际上&#xff0c;信号经过逻辑门需要一定的时间。不同路径上门电路数目不同…

【Qt】文件与音视频

目录 一、输入输出设备类 二、文件读写类 三、文件和目录信息类 四、音视频 4.1 音频 4.2 视频 文件操作是应用程序必不可少的部分。Qt作为一个通用开发库&#xff0c;提供了跨平台的文件操作能力。Qt提供了很多关于文件的类&#xff0c;通过这些类能够对文件系统进行操作…

LeetCode刷题之94.二叉树中序遍历

文章目录 1. 描述2. 分析2.1 递归方法2.2 迭代 3. 解答3.1 递归3.2 迭代 1. 描述 给定一个二叉树的根节点 root &#xff0c;返回 它的 中序 遍历 。 示例1&#xff1a; 输入&#xff1a;root [1,null,2,3] 输出&#xff1a;[1,3,2] 示例 2&#xff1a; 输入&#xff1a;ro…

浏览器工作原理与实践--页面性能:如何系统地优化页面

在前面几篇文章中&#xff0c;我们分析了页面加载和DOM生成&#xff0c;讨论了JavaScript和CSS是如何影响到DOM生成的&#xff0c;还结合渲染流水线来讲解了分层和合成机制&#xff0c;同时在这些文章里面&#xff0c;我们还穿插说明了很多优化页面性能的最佳实践策略。通过这些…

【C语言】扫雷小游戏

文章目录 前言一、游戏玩法二、创建文件test.c文件menu()——打印菜单game()——调用功能函数&#xff0c;游戏的实现main()主函数 game.c文件初始化棋盘打印棋盘随机布置雷的位置统计周围雷的个数展开周围一片没有雷的区域计算已排查位置的个数排查雷(包括检测输赢): game.h文…

【剪映专业版】04全局设置

视频课程&#xff1a;B站有知公开课【剪映电脑版教程】 设置-全局设置 草稿 草稿位置&#xff1a;非系统盘&#xff08;C盘&#xff09; 素材下载位置与 缓存管理&#xff1a;如果下载素材较多&#xff0c;需要定期删除缓存 预设保存位置&#xff1a;非系统盘&#xff08;C盘&a…

基于SpringBoot2.x、SpringCloud和SpringCloudAlibaba并采用前后端分离的企业级微服务多租户系统架构

简介 基于SpringBoot2.x、SpringCloud和SpringCloudAlibaba并采用前后端分离的企业级微服务多租户系统架构。并引入组件化的思想实现高内聚低耦合并且高度可配置化&#xff0c;适合学习和企业中使用。 真正实现了基于RBAC、jwt和oauth2的无状态统一权限认证的解决方案&#x…