文章目录
- 前言
- 设计数据库
- 物理设计
- 核心代码
- Dao层
- ProductDao.xml
- UserDao.xml
- Service层
- Controller层
- 总结
- 拦截器博客地址
前言
例如:随着互联网的不断发展,计算机语言这门技术也越来越重要,很多人都开启了计算机方面的学习,本文就简单介绍了一个简单的springboot项目的基础内容。
提示:以下是本篇文章正文内容,下面案例可供参考
设计数据库
数据库设计(Database Design)是指根据用户的需求,在某一具体的数据库管理系统上,设计数据库的结构和建立数据库的过程。
数据库设计是建立数据库及应用系统的技术,是信息系统开发和建议中的核心技术。由于数据库应用系统的复杂性,规划和结构化数据库中的数据对象以及这些数据对象之间关系的过程。
物理设计
用户实体:登录用户的信息,包括用户名、密码。
产品实体:用于更改商品,利率等。
产品表
use `test`;
DROP TABLE IF EXISTS `product`;
CREATE TABLE `product` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) DEFAULT NULL,
`rate` double DEFAULT NULL,
`amount` double DEFAULT NULL,
`raised` double DEFAULT NULL,
`cycle` int(11) DEFAULT NULL,
`endTime` char(10) DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;
INSERT INTO `product` VALUES (1,'天鑫添益2',2.76,50000,20000,30,'2022-07-10'),(2,'国泰添益',2.86,30000,30000,60,'2022-07-12'),(3,'国泰高鑫',2.55,60000,50000,90,'2022-07-09'),(4,'国福民安',2.96,30000,20000,7,'2022-05-10'),(5,'天益鑫多',2.65,80000,60000,20,'2022-07-05'),(6,'惠农收益',3.05,30000,20000,10,'2022-06-10'),(7,'惠农三鑫',2.76,50000,30000,30,'2022-07-02'),(8,'励学收益',2.86,30000,20000,20,'2022-07-11');
核心代码
yml文件
# 注册mybatis
mybatis:
# 注册映射文件
mapper-locations: classpath:com\abc\ssm\dao\*.xml
# 注册实体类别名
type-aliases-package: com.abc.ssm.bean
# 注册数据源
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql:///test?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&tinyInt1isBit=false
username: root
password: 20020702
# messages:
# basename: i18n/message
# encoding: utf-8
# #与Redis整合
redis:
host: redisOS
port: 6379
password: 111
#连接sentinel高可用集群
#sentinel:
#master: mymaster
#nodes: redisOS:26380,redisOS:26381,redisOS:26382
#连接Redis分布式系统
#cluster:
#nodes: redisOS:6380,redisOS:6381,redisOS:6382,redisOS:6383,redisOS:6384,redisOS:6385
cache:
type: redis
#
# #配置日志在控制台的显示格式
cache-names: gyf
#logging:
# pattern:
# console: gyf-%-5level -%msg%n
# level:
# #项目启动日志级别
# root: warn
# #指定包中代码运行的日志级别
# com.abc.ssm.dao: debug
pojo(产品实体类Product)
代码如下(示例):
package com.abc.ssm.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
// @NoArgsConstructor
// @AllArgsConstructor
public class Product implements Serializable{
private int id;
private String name; // 产品名称
private Double rate; // 年化利率
private Double amount; // 募集总金额
private Double raised; // 已募集金额
private Integer cycle; // 产品周期
private String endTime; // 募集结束日期
}
用户实体类User
代码如下(示例):
package com.abc.ssm.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User{
private int id;
private String username;
private String password;
private String role; //权限
}
用户输入用户名和密码即可进入首页,权限功能还有待开发。
Dao层
ProductDao接口
package com.abc.ssm.dao;
import com.abc.ssm.bean.Product;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface ProductDao {
Double selectTurnover(String date);//总金额查询
List<Product> selectAllProducts();//所有产品列表
List<Product> selectProductsByName(@Param("name") String name);//根据产品名称查询
int insertProduct(Product product);//新增产品
int deleteProduct(@Param("id") int id);//通过id删除产品
int updateProduct(Product product);//修改产品
//通过id查询一本书
Product selectProductById(@Param("id") int id);
}
UserDao接口
package com.abc.ssm.dao;
import com.abc.ssm.bean.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@Mapper
public interface UserDao {
public User Login(@Param("username") String username,
@Param("password") String password);
}
登录方法 通过数据库查询username和paswword。
mybatis xml
ProductDao.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.abc.ssm.dao.ProductDao">
<insert id="insertProduct" parameterType="Product">
insert into product(id,name, rate, amount, raised, cycle, endTime)
values(#{id},#{name}, #{rate}, #{amount}, #{raised}, #{cycle}, #{endTime})
</insert>
<select id="selectAllProducts" resultType="Product">
select id, name, rate, amount, raised, cycle, endTime
from product
</select>
<select id="selectProductsByName" resultType="Product">
select id, name, rate, amount, raised, cycle, endTime
from product
where name like '%' #{name} '%'
</select>
<select id="selectTurnover" resultType="double">
select SUM(raised)
from product
where endTime is not null and endTime < #{date}
</select>
<delete id="deleteProduct" parameterType="int">
delete from product where id=#{id}
</delete>
<select id="selectProductById" resultType="Product">
select * from product
where id=#{id}
</select>
<update id="updateProduct" parameterType="Product">
update product
set name=#{name},rate=#{rate},amount=#{amount},raised=#{raised},cycle=#{cycle},endTime=#{endTime}
where id=#{id}
</update>
</mapper>
- id:就是对应的namespace中的方法名;
- resultType:Sql语句执行的返回值!
- parameterType:参数类型!
UserDao.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.abc.ssm.dao.UserDao">
<select id="Login" resultType="User">
select * from user where username=#{username} and password=#{password}
</select>
</mapper>
Service层
Service层调用Dao层的方法
产品及用户Service接口
ProductService
package com.abc.ssm.service;
import com.abc.ssm.bean.Product;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface ProductService {
Double findTurnover();
List<Product> selectAllProducts();
List<Product> selectProductsByName(String name);
int insertProduct(Product product);
int deleteProduct(@Param("id") int id);
int updateProduct(Product product);
Product selectProductById(@Param("id") int id);
}
这个方法上面的ProductDao已经解释过了基本上和Dao层的引用一样。
- UserService
package com.abc.ssm.service;
import com.abc.ssm.bean.User;
public interface UserService {
public User Login(String username, String password);
}
产品及用户的接口实现类
- ProductServiceImpl
package com.abc.ssm.service.impl;
import com.abc.ssm.bean.Product;
import com.abc.ssm.dao.ProductDao;
import com.abc.ssm.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.BoundValueOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
@Service
public class ProductServiceImpl implements ProductService {
@Autowired
private ProductDao dao;
@Autowired
private RedisTemplate<Object, Object> template;
@Override
public Double findTurnover() {
// //获取redis操作对象
BoundValueOperations<Object, Object> ops = template.boundValueOps("turnover");
// //从缓存中获取数据
Object turnover = ops.get();
// //双重检测锁
if (turnover == null) {
synchronized (this) {
turnover = ops.get();
// //没有就从DB中查
if (turnover == null) {
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
turnover = dao.selectTurnover(sdf.format(date));
//将查询出的数据写入缓存
ops.set(turnover, 10, TimeUnit.SECONDS);
}
}
}
return (Double) turnover;
}
@Override
@Cacheable(value = "gyf", key = "'all-pros'")//缓存的名称
public List<Product> selectAllProducts() {
return dao.selectAllProducts();
}
@Override
@Cacheable(value = "gyf", key = "'name'")
public List<Product> selectProductsByName(String name) {
return dao.selectProductsByName(name);
}
@Override
@CacheEvict(value = "gyf",allEntries = true)//清除缓存
public int insertProduct(Product product) {
return dao.insertProduct(product);
}
@Override
@CacheEvict(value = "gyf",allEntries = true)//同上
public int deleteProduct(int id) {
return dao.deleteProduct(id);
}
@Override
@CacheEvict(value = "gyf",allEntries = true)
public int updateProduct(Product product) {
return dao.updateProduct(product);
}
@Override
@Cacheable(value = "gyf", key = "'id'")
public Product selectProductById(int id) {
return dao.selectProductById(id);
}
}
这个产品的实现类利用了双重检测锁,因为不管从数据库还是缓存查出来的数据都会有一定几率出现数据阻塞的情况。用redis这个中间件缓存查出来的数据,第一次查询是从数据库里面查出来然后放到redis缓存中,这样可以有效的减轻数据库查询数据的压力,以后用户在查询同样的数据,直接从缓存里面获取,时间快,效率高。这里用的时间格式是yyyy-MM-dd年月日的统一时间。
- UserServiceImpl
package com.abc.ssm.service.impl;
import com.abc.ssm.bean.User;
import com.abc.ssm.dao.UserDao;
import com.abc.ssm.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public User Login(String username, String password) {
return userDao.Login(username,password);
}
}
Controller层
Controller层调用Service层的方法,这里写的时候把User和Product写到一块了,建议分开写。
Controller
package com.abc.ssm.controller;
import com.abc.ssm.bean.Product;
import com.abc.ssm.bean.User;
import com.abc.ssm.service.ProductService;
import com.abc.ssm.service.UserService;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.ArrayList;
import java.util.List;
@Controller
@RequestMapping("/product")
public class ProductController {
@Autowired
private ProductService service;
@Autowired(required = false)
private UserService userService;
@RequestMapping("/toLogin")
public String toLogin(){
return "/login.jsp";
}
@RequestMapping("/login")
public String str(String username, String password, Model model,HttpSession session) {
User login = userService.Login(username, password);
if(login==null){
return "/login.jsp";
}
System.out.println("登陆成功!");
session.setAttribute("login",login);
return "/product/";
}
@RequestMapping("/")
public String index(Model model){
//查询平台的总交易额
Double turnover = service.findTurnover();
model.addAttribute("turnover",turnover);
//查询所有产品列表
List<Product> products = service.selectAllProducts();
model.addAttribute("products",products);
return "/index.jsp";
}
@RequestMapping("/selectProduct")
public String selectProductByName(String name,Model model) {
//通过名称查询产品,支持模糊查询
List<Product> list = service.selectProductsByName(name);
if (list == null) {
list = service.selectAllProducts();
model.addAttribute("error", "没有相关数据");
}
model.addAttribute("list", list);
model.addAttribute("name", name);
return "/result.jsp";
}
// 跳转到添加产品页面
@RequestMapping("/toAddProduct")
public String toAddPaper(){
return "/addProduct.jsp";
}
// 添加产品请求
@RequestMapping("/addProduct")
public String addProduct(Product product){
System.out.println("添加成功!");
service.insertProduct(product);
return "redirect:/product/";
}
// 跳转到修改页面
@RequestMapping("/toUpdateProduct/{id}")
public String toUpdatePaper(int id,Model model){
Product products= service.selectProductById(id);
model.addAttribute("products",products);
return "/updateBook.jsp";
}
//修改产品
@RequestMapping("/updateProduct")
public String updateBook(Product product){
System.out.println("updateBook=>"+product);//验证方法走到这一步了没有
service.updateProduct(product);
return "redirect:/product/";
}
//删除产品请求
@RequestMapping("/deleteProduct/{id}")
public String deleteProduct(int id){
System.out.println("删除成功!");
service.deleteProduct(id);
System.out.println("ok!");
return "redirect:/product/";
}
@RequestMapping("/logout")
public String logout(HttpSession session) {
session.removeAttribute("login");
return "redirect:/product/";
}
}
- System.out.println(“updateBook=>”+product)
这段代码是为了验证,建议大家写代码的时候出现bug可以把这段代码加进去查看方法是否走到了这一步。
拦截器
- 登录配置
package com.abc.ssm.config;
import com.abc.ssm.interceptor.UserLoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class LoginConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
//注册TestInterceptor拦截器
InterceptorRegistration registration = registry.addInterceptor(new UserLoginInterceptor());
registration.addPathPatterns("/**"); //所有路径都被拦截
registration.excludePathPatterns( //添加不拦截路径
"/product/login", //登录路径
"/**/*.jsp", //html静态资源
"/**/*.js", //js静态资源
"/**/*.css" //css静态资源
);
}
}
拦截器思想
1、SpringBoot实现登录拦截的原理
SpringBoot通过实现HandlerInterceptor接口实现拦截器,通过实现WebMvcConfigurer接口实现一个配置类,在配置类中注入拦截器,最后再通过@Configuration注解注入配置.
1.1、实现HandlerInterceptor接口
实现HandlerInterceptor接口需要实现3个方法:preHandle、postHandle、afterCompletion.
preHandle在Controller之前执行,因此拦截器的功能主要就是在这个部分实现:
检查session中是否有user对象存在;
如果存在,就返回true,那么Controller就会继续后面的操作;
如果不存在,就会重定向到登录界面
就是通过这个拦截器,使得Controller在执行之前,都执行一遍preHandle.
1.2、实现WebMvcConfigurer接口,注册拦截器
实现WebMvcConfigurer接口来实现一个配置类,将上面实现的拦截器的一个对象注册到这个配置类中.
将拦截器注册到了拦截器列表中,并且指明了拦截哪些访问路径,不拦截哪些访问路径,不拦截哪些资源文件;最后再以@Configuration注解将配置注入。
1.3、保持登录状态
只需一次登录,如果登录过,下一次再访问的时候就无需再次进行登录拦截,可以直接访问网站里面的内容了。
在正确登录之后,就将user保存到session中,再次访问页面的时候,登录拦截器就可以找到这个user对象,就不需要再次拦截到登录界面了.
拦截器实现
package com.abc.ssm.interceptor;
import com.abc.ssm.bean.User;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class UserLoginInterceptor implements HandlerInterceptor {
/***
* 在请求处理之前进行调用(Controller方法调用之前)
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("执行了拦截器的preHandle方法");
try {
HttpSession session = request.getSession();
User user = (User) session.getAttribute("login");
if (user != null) {
return true;
}
response.sendRedirect(request.getContextPath() + "/product/login");
} catch (Exception e) {
e.printStackTrace();
}
return false;
//如果设置为false时,被请求时,拦截器执行到此处将不会继续操作
//如果设置为true时,请求将会继续执行后面的操作
}
/***
* 请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("执行了拦截器的postHandle方法");
}
/***
* 整个请求结束之后被调用,也就是在DispatchServlet渲染了对应的视图之后执行(主要用于进行资源清理工作)
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("执行了拦截器的afterCompletion方法");
}
}
总结
mybatis配置需要认真小心的写,因为这个关系到数据库的正常增删改查,然后就是项目增删改查的实现类需要考虑到缓存阻塞的问题,可以获取redis的操作对象进行改进。最后拦截器的配置及用法需要仔细反复琢磨。整体下来一个简单的springboot的增删改查小项目就算完成了。