马儿农场系统开发(3)—— 后端设计及实现

写在前头

  • 回到单位事情太多了,只能用spring boot凑合着点弄了个后端出来,主要是同数据库和消息队列的交互

结构设计

数据库设计

消息队列设计

技术选型

  • 开发工具:IDEA
  • 运行环境:Jar包内置的Tomcat
  • 构建工具:Maven
  • java框架:Spring Boot
  • 持久层框架:Mybatis
  • 数据库连接池:Druid
  • 关系型数据库:Mysql
  • 缓存型数据库:Redis
  • 消息队列:RabbitMQ
  • 数据库表设计:PandMan
  • 测试工具:PostMan

代码

redis交互

配置

  • 需要写个配置类,为redisTemplate增加序列化的配置,使其支持存储java对象
package com.example.horseback.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(connectionFactory);

        // 使用Jackson2JsonRedisSerialize替换默认序列化
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);

        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);

        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);

        // 设置key和value的序列化规则
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.afterPropertiesSet();

        return redisTemplate;
    }
}

dao层

package com.example.horseback.dao;

import jakarta.annotation.Resource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Repository;

import java.io.Serializable;
import java.lang.invoke.SerializedLambda;

@Repository
public class RedisUtils {
    @Resource
    private RedisTemplate redisTemplate;

    public boolean set(final String key, Object value){
        boolean result = false;
        try {
            ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
            operations.set(key, value);
            result = true;
        } catch (Exception e){
            e.printStackTrace();
        }
        return result;
    }

    public String get(final String key){
        Object result = null;
        ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
        result = operations.get(key);
        return result.toString();
    }

    public void remove(final String key){
        if (exists(key)){
            redisTemplate.delete(key);
        }
    }

    public void remove(final String... keys){
        for (String key: keys){
            remove(key);
        }
    }

    public boolean exists(final String key){
        return redisTemplate.hasKey(key);
    }
}

service层

package com.example.horseback.service;

import com.example.horseback.dao.RedisUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class HorseStateService {
    @Autowired
    private RedisUtils redisUtils;
    private String key = "state";

    public void setActiveState() {
        redisUtils.set(key, 1);
    }

    public void setDeadState() {
        redisUtils.set(key, 0);
    }

    public String getState() {
        return redisUtils.get(key);
    }
}

controller层

package com.example.horseback.controller;

import com.example.horseback.service.HorseStateService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HoseStateController {
    @Autowired
    private HorseStateService horseStateService;

    @RequestMapping("/setStateActive")
    public void setStateActive(){
        horseStateService.setActiveState();
    }

    @RequestMapping("/setStateDead")
    public void setStateDead(){
        horseStateService.setDeadState();
    }

    @RequestMapping("/getState")
    public String getState(){
        return horseStateService.getState();
    }
}

mysql交互

表设计

  • 首先在Mysql中增加user_table表,为其添加user_id,user_name和user_pwd三个字段

entity层

  • alt+insert自动生成getter和setter以及构造方法
package com.example.horseback.entity;

public class User {
    private Integer user_id;
    private String user_name;
    private String user_pwd;

    public User(Integer user_id, String user_name, String user_pwd){
        this.user_id = user_id;
        this.user_name = user_name;
        this.user_pwd = user_pwd;
    }

    public Integer getUser_id() {
        return user_id;
    }

    public void setUser_id(Integer user_id) {
        this.user_id = user_id;
    }

    public String getUser_name() {
        return user_name;
    }

    public void setUser_name(String user_name) {
        this.user_name = user_name;
    }

    public String getUser_pwd() {
        return user_pwd;
    }

    public void setUser_pwd(String user_pwd) {
        this.user_pwd = user_pwd;
    }
}

mapper层

  • 建议将mybatis-mapper.xml放到resources/mapper目录下,方便其他模块引用
  • 如果xml放到resources目录下,需要在application.properties中添加一行mapper-locations: classpath:mapper/*.xml,指明引用位置
<mapper namespace="com.example.horseback.dao.UserDao">
    <resultMap id="BaseResultMap" type="com.example.horseback.entity.User">
        <result column="user_id" property="user_id" />
        <result column="user_name" property="user_name" />
        <result column="user_pwd" property="user_pwd" />
    </resultMap>
    <select id="findById" resultMap="BaseResultMap">
        select * from user_table where user_id = #{user_id}
    </select>
    <select id="findWhenLogIn" resultMap="BaseResultMap">
        select * from user_table where user_name = #{user_name} and user_pwd = #{user_pwd}
    </select>
    <insert id="addUser" parameterType="com.example.horseback.entity.User">
        insert into user_table (user_name, user_pwd) values (#{user_name}, #{user_pwd})
    </insert>
</mapper>

dao层

package com.example.horseback.dao;

import com.example.horseback.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.stereotype.Repository;

@Repository
public interface UserDao {
    User findById(int user_id);
    User findWhenLogIn(String user_name, String user_pwd);
    Long addUser(String user_name, String user_pwd);
}

service层

package com.example.horseback.service;

import com.example.horseback.dao.UserDao;
import com.example.horseback.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {
     @Autowired
     UserDao userDao;

     public User getLoginUser(String userName, String passWord){
         return userDao.findWhenLogIn(userName, passWord);
     }

     public User getUserById(Integer id){
         return userDao.findById(id);
     }

     public long addUser(String userName, String passWord){
         return userDao.addUser(userName, passWord);
     }
}

controller层

  • 用来做登录控制以及添加用户
package com.example.horseback.controller;

import com.example.horseback.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class LoginController {
    @Autowired
    UserService userService;

    @RequestMapping("/login")
    public String doLogin(String userName, String passWord){
        if (userService.getLoginUser(userName, passWord) != null){
            return "登录成功!";
        }
        else {
            return "用户名或密码错误!";
        }
    }

    @RequestMapping("/addUser")
    public String doAdd(String userName, String passWord){
        if(userService.addUser(userName, passWord) == 1){
            return "成功插入用户!";
        }
        else{
            return "插入用户失败";
        }
    }
}

问题

  • 在dao层中定义数据库接口方法时,有两种方法让IOC扫描到定义的方法
    • 可以在UserDao类上添加@mapper注解,实际调试了很久发现不行
    • 在启动类上添加注解@MapperScan(value = "com.example.horseback.dao"),指明mapper方法的路径,实际调试可行
  • 报错提示sqlSessionFactory or sqlSessionTemplate are required,解决办法
    • 网上说是mybatis-starter版本过高,我在pom.xml中试了各种历史版本,还是不行
    • 创建一个dao层的基类,在基类中注入sqlSessionFactory,然后让自己的dao去继承这个基类,但是由于我的dao层只是接口,故该方法不行
    • 将application.properties改为application.yml,取消自动注入,@EnableAutoComfiguration(exclude=DataSourceAutoConfiguration.class)

消息队列交互

  • 使用routing模型,如果要设置动态路由,应该用topic模型
  • 貌似不能写到controller里,否则在自动注入时会报rabbitTemplate指针为空,要把读写队列的代码写到测试方法里,用SprinBootTest和RunWith注解才行,但在测试方法里我就写不了路由了,有没有大佬有对策?
package com.example.horseback.controller;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
public class MQProcessController {
    @Resource
    public RabbitTemplate rabbitTemplate;

    @RequestMapping("/writeQueue")
    public String writeQueueToRouting(String msg, String routingKey) {
        rabbitTemplate.convertAndSend("directs",routingKey, msg);
        return "发送成功";
    }

    @RequestMapping("/readQueue")
    @RabbitListener(bindings = {
            @QueueBinding(
                    value = @Queue,  //创建一个临时队列
                    exchange = @Exchange(value = "directs",type = "direct"),
                    key = {"test"}
            )
    })
    public void readQueueToRouting(String message){
        System.out.println("message1 = " + message);
    }
}

配置文件

  • application.properties
server.port=8080

redis:
    host:    
    jedis:
    pool:
        max-active: 8
        max-wait: -1
        max-idle: 500
        min-idle: 0
    lettuce:
        shutdown-timeout: 30000

spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.password=guest
spring.rabbitmq.username=guest
spring.rabbitmq.listener.simple.acknowledge-mode=manual
spring.rabbitmq.listener.direct.acknowledge-mode=manual
spring.rabbitmq.listener.simple.prefetch=1

spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/ssm-java1?useSSL=false
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password:

mybatis:
    mapper-locations: classpath:mapper/*.xml
posted @ 2023-02-02 20:28  z5onk0  阅读(36)  评论(0编辑  收藏  举报