一、静态资源映射
1、介绍:
静态资源,例如 JS、CSS 和 HTML 等需要配置静态资源的映射才能被访问到。()
2、实现方式
-
webjars方式
引入依赖
<dependency> <groupId>org.webjars</groupId> <artifactId>jquery</artifactId> <version>3.4.1</version> </dependency>
SpringBoot对于静态资源放置的位置,是有静态资源映射规则,在SpringBoot中,SpringMVC的web配置都在 org.springframework.boot.autoconfigure.web.servlet 包下的 WebMvcAutoConfiguration 这个配置里面,我们找到addResourceHandlers这个方法,该方法用于处理webjars方式的静态资源映射。
@Override protected void addResourceHandlers(ResourceHandlerRegistry registry) { super.addResourceHandlers(registry); if (!this.resourceProperties.isAddMappings()) { logger.debug("Default resource handling disabled"); return; } ServletContext servletContext = getServletContext(); addResourceHandler(registry, "/webjars/**","classpath:/META-INF/resources/webjars/"); addResourceHandler(registry,this.mvcProperties.getStaticPathPattern(),registration -> { registration.addResourceLocations(this.resourceProperties.getStaticLocations()); if (servletContext != null) { registration.addResourceLocations(new ServletContextResource(servletContext, SERVLET_LOCATION)); } }); }
-
继承WebMvcConfigurationSupport类
@Configuration public class WebMvcConfig extends WebMvcConfigurationSupport { /** * 添加静态资源映射 * @param registry */ @Override protected void addResourceHandlers(ResourceHandlerRegistry registry) { // 表示通过 ip:port/profile/** 能访问服务器D://upload目录下的所有文件 // addResourceHandler是指你想在url请求的路径 // addResourceLocations是存放的真实路径 registry.addResourceHandler("/profile/**").addResourceLocations("file:D://upload"); // 表示通过 ip:port/backend/** 能访问根目录/backend目录下的所有文件 registry.addResourceHandler("/backend/**").addResourceLocations("classpath:/backend/"); // 重写父类方法默认映射关系,如果不需要可以不写 registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/"); } }
-
在application.properties中配置
# 自定义静态资源访问路径,可以指定多个,之间用逗号隔开 spring.resources.static-locations=classpath:/file1/,classpath:/file2
二、过滤器
1、介绍
过滤器 Filter 由 Servlet 提供,基于函数回调实现链式对网络请求与响应的拦截与修改。
Filter 的生命周期
init(): 初始化Filter 实例,Filter 的生命周期与 Servlet 是相同的,也就是当 Web 容器(tomcat)启动时,调用 init() 方法初始化实例,Filter只会初始化一次。需要设置初始化参数的时候,可以写到init()方法中。 doFilter(): 业务处理,拦截要执行的请求,对请求和响应进行处理,一般需要处理的业务操作都在这个方法中实现 destroy() : 销毁实例,关闭容器时调用 destroy() 销毁 Filter 的实例。
2、实现方式
-
@WebFilter注解
通过 @WebFilter 注解,将类声明为 Bean 过滤器类,在启动类添加注解 @ServletComponentScan ,让 Spring 可以扫描到。
/** * 检查用户是否完成登录 */ @WebFilter(filterName = "loginCheckFilter",urlPatterns = "/*") @Slf4j public class LoginCheckFilter implements Filter { //路径匹配器,支持通配符 //在当前的作用域中创建了一个名为PATH_MATCHER的常量,并将其赋值为AntPathMatcher类的一个新实例。 //是Spring框架中的一个工具类,用于匹配和操作Ant样式的路径模式。它可以用于实现路径匹配、路径变量提取等功能 public static final AntPathMatcher PATH_MATCHER =new AntPathMatcher(); @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request =(HttpServletRequest) servletRequest; HttpServletResponse response =(HttpServletResponse) servletResponse; //1、获取本次请求uri,将不需要的拦截的放行 String requestURI = request.getRequestURI(); log.info("拦截到请求:{}",requestURI); //定义不需要处理的路径 String[] urls = new String[]{ "/employee/login", "/employee/logout", "/backend/**", "front/**" }; //2、判断本次请求是否需要处理 boolean check = check(urls, requestURI); //3、如果不需要处理,则直接放行 if(check){ log.info("本次请求{}不需要处理",requestURI); // 放行 filterChain.doFilter(request,response); return; } //4、判断登录状态,如果已登录,则直接放行 if(request.getSession().getAttribute("employee")!=null){ log.info("用户已登录,用户id为:{}",request.getSession().getAttribute("employee")); // 放行 filterChain.doFilter(request,response); return; } log.info("用户未登录"); // 5、如果未登录则返回登录结果(分析前端代码)通过输出流方式向客户端响应数据 // response.getWriter()返回的是一个字符输出流(PrintWriter), // 通过调用其write()方法可以将字符串写入到HTTP响应的输出流中。 response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN"))); return; } /** * 路径匹配,检查本次请求是否需要放行 * @param urls * @param requestURI * @return */ public boolean check(String[]urls,String requestURI){ for (String url:urls){ boolean match = PATH_MATCHER.match(url,requestURI); if (match){ return true; } } return false; } @SpringBootApplication @ServletComponentScan public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
三、拦截器
1、介绍
拦截器是在servlet执行之前执行的程序(这里就是controller代码执行之前),它主要是用于拦截用户请求并作相应的处理,比如说可以判断用户是否登录,做相关的日志记录,也可以做权限管理。
SpringBoot中的拦截器实现和spring mvc 中是一样的,它的大致流程是,先自己定义一个拦截器类,并将这个类实现一个HandlerInterceptor类,或者是继承HandlerInterceptorAdapter,都可以实现拦截器的定义。然后将自己定义的拦截器注入到适配器中,也有两种方式,一种是实现WebMvcConfigurer接口,一种是继承WebMvcConfigurerAdapter。
2.自定义拦截器
public class MyInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { System.out.println("进入拦截器了"); //中间写逻辑代码,比如判断是否登录成功,失败则返回false return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) { // System.out.println("controller 执行完了"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { System.out.println("我获取到了一个返回的结果:"+response); System.out.println("请求结束了"); } }
代码说明:
-
自定义的拦截器可以实现HandlerInterceptor接口,也可以继承HandlerInterceptorAdapter类。
-
重写三个方法,当然也可以只实现一个最重要的preHandle方法。
-
preHandle方法:此方法会在进入controller之前执行,返回Boolean值决定是否执行后续操作。
-
postHandle方法:此方法将在controller执行之后执行,但是视图还没有解析,可向ModelAndView中添加数据(前后端不分离的)。
-
afterCompletion方法:该方法会在整个请求结束(请求结束,但是并未返回结果给客户端)之后执行, 可获取响应数据及异常信息。
3.拦截器注入适配器
@Configuration public class InterceptorConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new MyInterceptor()) .addPathPatterns("/**")//拦截所有的路径 .excludePathPatterns("/LoginController/login"); } }
四、异常处理
1、介绍
@RestControllerAdvice是什么 @RestControllerAdvice是一个组合注解,由@ControllerAdvice、@ResponseBody组成,而@ControllerAdvice继承了@Component,因此@RestControllerAdvice本质上是个Component,用于定义@ExceptionHandler,@InitBinder和@ModelAttribute方法,适用于所有使用@RequestMapping方法。
@RestControllerAdvice的特点:
通过@ControllerAdvice注解可以将对于控制器的全局配置放在同一个位置。 注解了@RestControllerAdvice的类的方法可以使用@ExceptionHandler、@InitBinder、@ModelAttribute注解到方法上。 @RestControllerAdvice注解将作用在所有注解了@RequestMapping的控制器的方法上。 @ExceptionHandler:用于指定异常处理方法。当与@RestControllerAdvice配合使用时,用于全局处理控制器里的异常。 @InitBinder:用来设置WebDataBinder,用于自动绑定前台请求参数到Model中。 @ModelAttribute:本来作用是绑定键值对到Model中,当与@ControllerAdvice配合使用时,可以让全局的@RequestMapping都能获得在此处设置的键值对
2、实现方式
-
直接处理
/** * 注解参数 annotations 指定了需要处理的控制器的类型。 * annotations = {} 表示该全局异常处理类将会处理使用了 * {}内注解的控制器类。 */ @RestControllerAdvice(annotations = {RestController.class, Controller.class}) @Slf4j public class GlobalExceptionHandler { /** * 异常处理方法 * */ //ExceptionHandler()当遇到()内里面的异常时执行下面方法 @ExceptionHandler(SQLIntegrityConstraintViolationException.class) public R<String> exceptionHandler(SQLIntegrityConstraintViolationException ex){ log.info(ex.getMessage()); return R.error("失败了‘"); } }
-
自定义异常
//自定义业务异常 public class CustomException extends RuntimeException{ public CustomException(String message){ super(message); } }
/** * 注解参数 annotations 指定了需要处理的控制器的类型。 * annotations = {} 表示该全局异常处理类将会处理使用了 * {}内注解的控制器类。 */ @RestControllerAdvice(annotations = {RestController.class, Controller.class}) @Slf4j public class GlobalExceptionHandler { @ExceptionHandler(CustomException.class) public R<String> exceptionHandler(CustomException ex){ log.info(ex.getMessage()); return R.error(ex.getMessage()); } }
五、Mybatis-plus分页查询
1、介绍:
在实现分页查询时,MyBatis-Plus要求你添加一个分页拦截器(PaginationInterceptor),这是因为分页查询涉及到对SQL语句的修改和重写,以实现正确的分页效果。分页拦截器是MyBatis-Plus提供的一个组件,它会拦截执行的SQL语句,并根据指定的分页参数,修改SQL语句以获取指定范围的数据。
2、实现方式
-
先配置分页拦截器
@Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor(){ MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor(); mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor()); return mybatisPlusInterceptor; } }
-
再实现分页功能
/** * 分页 * @param page * @param pageSize * @param name * @return */ public R<Page> page(int page,int pageSize,String name){ log.info("page={},pageSize={},name={}",page,pageSize,name); //构造分页构造器 Page pageInfo = new Page(page, pageSize); //构造条件构造器 LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper(); //添加过滤条件 // StringUtils.isNotEmpty(name)判断那么是否为空,如果为空则不执行查询语句,如果不为空则执行查询语句 queryWrapper.like(StringUtils.isNotEmpty(name),Employee::getName,name); //添加排序条件 queryWrapper.orderByDesc(Employee::getUpdateTime); //mybatis-plus自带的分页 employeeService.page(pageInfo,queryWrapper); return R.success(pageInfo); }
六、处理长整型精度丢失问题
-
设置消息转换器
/** * 对象映射器:基于jackson将Java对象转为json,或者将json转为Java对象 * 将JSON解析为Java对象的过程称为 [从JSON反序列化Java对象] * 从Java对象生成JSON的过程称为 [序列化Java对象到JSON] */ public class JacksonObjectMapper extends ObjectMapper { public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd"; public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss"; public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss"; public JacksonObjectMapper() { super(); //收到未知属性时不报异常 this.configure(FAIL_ON_UNKNOWN_PROPERTIES, false); //反序列化时,属性不存在的兼容处理 this.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); SimpleModule simpleModule = new SimpleModule() .addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT))) .addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT))) .addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT))) .addSerializer(BigInteger.class, ToStringSerializer.instance) .addSerializer(Long.class, ToStringSerializer.instance) .addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT))) .addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT))) .addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT))); //注册功能模块 例如,可以添加自定义序列化器和反序列化器 this.registerModule(simpleModule); } }
-
将消息转化器设置在配置类中生效
WebMvcConfig
/** * 扩展mvc框架的消息转换器 * @param converters */ @Override protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) { // 创建消息转换器对象 MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter(); // 设置对象转换器 messageConverter.setObjectMapper(new JacksonObjectMapper()); // 将上面的消息转换器对象追加到mvc框架的转换器集合中 converters.add(0,messageConverter); }
七、公共字段填充
基于mybatis-plus
-
在实体类中设置自动填充策略
@TableField(fill = FieldFill.INSERT)//插入时自动填充 private LocalDateTime createTime; @TableField(fill = FieldFill.INSERT_UPDATE)//插入和更新时自动填充 private LocalDateTime updateTime; @TableField(fill = FieldFill.INSERT) private Long createUser; @TableField(fill = FieldFill.INSERT_UPDATE) private Long updateUser;
-
基于线程获取id
/** * 基于ThreadLocal封装工具类,用户保存和获取当前登录用户id */ public class BaseContext { private static ThreadLocal<Long> threadLocal = new ThreadLocal<>(); /** * 获取并设置id * @param id */ public static void setCurrentId(Long id){ threadLocal.set(id); } /** * 获取id值 * @return */ public static Long getCurrentId(){ return threadLocal.get(); } }
-
编辑元数据对象处理器
/** * 元数据对象处理器 */ @Component @Slf4j public class MyMetaObjecthandler implements MetaObjectHandler { /** * 插入操作自动填充 * @param metaObject */ @Override public void insertFill(MetaObject metaObject) { log.info("公共字段填充[insert]..."); log.info(metaObject.toString()); metaObject.setValue("createTime", LocalDateTime.now()); metaObject.setValue("updateTime", LocalDateTime.now()); metaObject.setValue("createUser", BaseContext.getCurrentId());//通过线程获取的id metaObject.setValue("updateUser", BaseContext.getCurrentId()); } /** * 更新操作自动填充 * @param metaObject */ @Override public void updateFill(MetaObject metaObject) { log.info("公共字段更新[update]..."); log.info(metaObject.toString()); long id = Thread.currentThread().getId(); log.info("线程id为:{}",id); metaObject.setValue("updateTime", LocalDateTime.now()); metaObject.setValue("updateUser", BaseContext.getCurrentId());//通过线程获取的id } }
八、文件的上传和下载
-
在yml配置文件设置通用路径,方便日后修改。
reggie: path: E:\瑞吉外卖\image\
-
上传文件实现
@Value("${reggie.path}") private String basePath; //文件上传 @PostMapping("/upload") //MultipartFile file就是文件上传参数,file必须和浏览器发来的name一致 public R<String> upload(MultipartFile file) { //file 是一个临时文件,需要转存到指定位置,否则请求完成后临时文件会删除 log.info("file:{}", file.toString()); //原始文件名 String originalFilename = file.getOriginalFilename(); //截取后缀名 String suffix = originalFilename.substring(originalFilename.lastIndexOf(".")); //使用uuid重新生成文件名,防止文件名重复 String fileName = UUID.randomUUID().toString() + suffix; //创建一个目录对象 File dir = new File(basePath); //判断当前目录是否存在 if (!dir.exists()) { //目录不存在则创建 dir.mkdirs(); } try { //指定文件位置(主要就是这里) file.transferTo(new File(basePath + fileName)); } catch (IOException e) { e.printStackTrace(); } return R.success(fileName); }
-
下载文件实现
@GetMapping("/download") public void download(String name, HttpServletResponse response) { try { //输入流通过输入流读文件 FileInputStream fileInputStream = new FileInputStream(new File(basePath + name)); //输出流,通过输出流将文件写回浏览器,在浏览器中显示图片 ServletOutputStream outputStream = response.getOutputStream(); response.setContentType("image/jpeg"); int len = 0; byte[] bytes = new byte[1024]; while ((len = fileInputStream.read(bytes)) != -1) { outputStream.write(bytes, 0, len); outputStream.flush(); } } catch (Exception e) { e.printStackTrace(); } }
八、双表增删改
增删改方式类似,这里就写一个增加为例
-
设置一个Dto用于联系双表
@Data public class DishDto extends Dish { private List<DishFlavor> flavors = new ArrayList<>(); private String categoryName; private Integer copies; }
-
在Service实现层处理
@Autowired private DishFlavorService dishFlavorService; /** * 新增菜品,同时保存对应的口味数据 * * @param dishDto */ @Transactional //通过dishdto将数据分别保存到两个表中 public void saveWithFlavor(DishDto dishDto) { //保存菜品的基本信息到菜品表dish this.save(dishDto); //此时我们还未获取到DishFlavors表中的dishId。那么我们可以通过dto中的列表来传入 //获取到菜品id,这里是dish表中的id Long dishId = dishDto.getId();//菜品id //菜品口味,将dish表中的id传给flavors表 List<DishFlavor> flavors = dishDto.getFlavors(); flavors = flavors.stream().map((item) -> { item.setDishId(dishId); return item; //这里是以列表返回 }).collect(Collectors.toList()); //将id和用户输入的数据保存菜品口味数据到菜品口味表dish_flavor // saveBatch传列表 dishFlavorService.saveBatch(flavors); }
-
在controller层调用
@PostMapping public R<String> save(@RequestBody DishDto dishDto){ log.info(dishDto.toString()); dishService.saveWithFlavor(dishDto); return R.success("新增菜品成功"); }
-
注意多变查询要增加事务处理
运行类中添加@EnableTransactionManagement注解
实现类上添加@Transactional注解
九、实现双表分页查询
根据id查询方式与其类似就不写了
1、使用dto关联两表数据
@Data public class DishDto extends Dish { private List<DishFlavor> flavors = new ArrayList<>(); private String categoryName; private Integer copies; }
2、在controller层中进行数据处理
@GetMapping("/page") public R<Page> page(int page, int pageSize, String name) { //构造分页构造器对象 Page<Dish> pageInfo = new Page<>(page, pageSize); Page<DishDto> dishDtoPage = new Page<>(); //条件构造器 LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper<>(); //添加过滤条件 queryWrapper.like(name != null, Dish::getName, name); //添加排序条件 queryWrapper.orderByDesc(Dish::getUpdateTime); //执行分页查询 dishService.page(pageInfo, queryWrapper); /** * 经过分页后缺少一个菜品分类的字段 * 这里是获取这个字段 *最终分页自动获取records内容 */ //对象拷贝(注意这里只是拷贝的属性,并不是具体数据内容) //BeanUtils.copyProperties(源,拷贝到的对象,忽略的字段);records是内容的列表 //records这里忽略的原因是dishDto中的内容属性和dish中的不同,不能直接拷过去 BeanUtils.copyProperties(pageInfo, dishDtoPage, "records"); //把分页中的内容传回给dish列表 List<Dish> records = pageInfo.getRecords(); //通过流的形式遍历刚获得的内容,处理完成后用DishDto列表接收 List<DishDto> list = records.stream().map((item) -> { //创建DishDto对象 DishDto dishDto = new DishDto(); //将内容先拷贝过去给这个DishDto对象 BeanUtils.copyProperties(item, dishDto); //获取分类id,两表具有的相同字段分类id,可通过分类id来查询数据 Long categoryId = item.getCategoryId();//分类id //根据id查询分类对象 Category category = categoryService.getById(categoryId); //如果不为空通过查询的信息来获取菜品分类,将菜品分类名传给DishDto if (category != null) { String categoryName = category.getName(); dishDto.setCategoryName(categoryName); } return dishDto; //将内容收集成列表 }).collect(Collectors.toList()); dishDtoPage.setRecords(list); return R.success(dishDtoPage); }
十、redis的使用
1、配置redis
-
导入坐标
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
-
配置yml文件
redis: host: localhost port: 6379 password: database: 0
2、实现redis缓存
注入redis对象
@Autowired private RedisTemplate redisTemplate;
设置缓存数据和时间等信息
redisTemplate.opsForValue().set(phone,code,5, TimeUnit.MINUTES);
十一、Spring Cache
1、介绍
说白了就是redis注解
Spring cache是一个框架,实现了基于注解的缓存功能,只需要简单地加一个注解,就能实现缓存功能。
Spring Cache提供了一层抽象,底层可以切换不同的cache实现。具体就是通过CacheManager接口来统一不同的缓存技术。
CacheManager是Spring提供的各种缓存技术抽象接口。
针对不同的缓存技术需要实现不同的CacheManager:
CacheManager | 描述 |
---|---|
EhCacheCacheManager | 使用EhCache作为缓存技术 |
GuavaCacheManager | 使用Google的GuavaCache作为缓存技术 |
RedisCacheManager | 使用Redis作为缓存技术 |
2、使用方法
Spring Cache常用注解
3、实现方式
-
导入maven坐标
spring-boot-starter-data-redis、spring-boot-starter-cache
-
配置yml
spring: cache: redis: time-to-live: 1800000#设置缓存有效期
-
在启动类上加入@EnableCaching注解,开启缓存注解功能
-
在Controller的方法上加入@Cacheable、@CacheEvict等注解,进行缓存操作
eg:
@Cacheable(value = "setmealCache",key = "#setmeal.categoryId+'_'+#setmeal.status") @CacheEvict(value = "setmealCache",allEntries = true)
注意要让通用实体类R实现Serializable接口(序列化),注解才能生效
十二、配置主从库
1、介绍
MysSQL主从复制是一个异步的复制过程,底层是基于Mysql数据库自带的二进制日志功能。就是一台或多台AysQL数据库(slave,即从库)从另一台MysQL数据库(master,即主库)进行日志的复制然后再解析日志并应用到自身,最终实现从库的数据和主库的数据保持一致。MySQL主从复制是MysQL数据库自带功能,无需借助第三方工具。
2、配置方法
-
声明:主库Master,从库slave。提前准备好两台服务器,分别安装Mysql并启动服务成功
实现过程:
master将改变记录到二进制日志( binary log)
slave将master的binary log拷贝到它的中继日志(relay log)
slave重做中继日志中的事件,将改变应用到自己的数据库中
-
主库配置方法
第一步:修改Mysq1数据库的配置文件/etc/my.cnf
[mysqld] log-bin=mysql-bin #[必须]启用二进制日志 server-id=100 #[必须]服务器唯一ID
第二步:重启Mysql服务 systemctl restart mysqld
第三步:登录Mysql数据库,执行下面SQL(建一个用户用作管理)
GRANT REPLICATION SLAVE ON *.* to 'xiaoming'@'%' identified by 'Root@123456';
注:上面SQL的作用是创建一个用户xiaoming,密码为Root@123456,并且给xiaoming用户授予REPLICATION SLAVE权限。常用于建立复制时所需要用到的用户权限,也就是slave必须被master授权具有该权限的用户,才能通过该用户复制。
第四步:登录Mysql数据库,执行下面SQL,记录下结果中File和Position的值
show master status;
编辑
注:上面SQL的作用是查看Master的状态,执行完此SQL后不要再执行任何操作
-
配置-从库Slave
克隆的话记得修改uuid
第一步:修改Mysq1数据库的配置文件/etc/my.cnf
[mysqld] server-id=101 #[必须]服务器唯一ID
第二步:重启Mysql服务
systemctl restart mysqld
第三步:登录Mysq1数据库,执行下面SQL
change master to master_host='主库ip',master_user='xiaoming',master_password='Root@123456',master_log_file='mysql-bin.000003',master_log_pos=441; start slave;
第四步:登录Mysql数据库,执行下面SQL,查看从数据库的状态show slave status;
-
Sharding-JDBC介绍
Sharding-JDBC定位为轻量级Java框架,在Java的JDBC层提供的额外服务。它使用客户端直连数据库,以jar包形式提供服务,无需额外部署和依赖,可理解为增强版的JDBC驱动,完全兼容JDBC和各种ORM框架。
使用Sharding-JDBC可以在程序中轻松的实现数据库读写分离。
-
适用于任何基于JDBC的ORM框架,如: JPA, Hibernate,Mybatis, Spring JDBC Template或直接使用JDBC。
-
支持任何第三方的数据库连接池,如:DBCP,C3PO,BoneCP, Druid, HikariCP等。
-
支持任意实现JDBC规范的数据库。目前支持MySQL,Oracle,SQLServer,PostgreSQL以及任何遵循SQL92标准的数据库。
3、实现方式
-
导入maven坐标
<dependency> <groupId>org.apache.shardingsphere</groupId> <artifactId>sharding-jdbc-spring-boot-starter</artifactId> <version>4.0.0-RC1</version> </dependency>
-
在配置文件中配置读写分离规则
spring: shardingsphere: datasource: names: master,slave # 主数据源 master: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://(主库ip地址):3306/rw?characterEncoding=utf-8 username: root password: 123456 # 从数据源 slave: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://(从ip地址):3306/rw?characterEncoding=utf-8 username: root password: 123456 masterslave: # 读写分离配置 load-balance-algorithm-type: round_robin #轮询 # 最终的数据源名称 name: dataSource # 主库数据源名称 master-data-source-name: master # 从库数据源名称列表,多个逗号分隔 slave-data-source-names: slave props: sql: show: true #开启SQL显示,默认false
-
在配置文件中配置允许bean定义覆盖配置项
spring: main: allow-bean-definition-overriding: true