chpt1 项目框架搭建

1.1 springboot环境搭建

springMVC需要大量配置(ViewResolver等),spring追求“零配置”。
spring官网https://spring.io/ (project—>SpringBoot)

第一步 加依赖
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.5.0</version>
  </parent>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

新建项目的随手截图

springboot整合hiberate_User


*IDEA的project可以包含多个model,可直接导入model项目,model相当于Eclipse中的project。

第二步启动类
@SpringBootApplication
public class MainApplication{
    public static void main(String[] args) {
        SpringApplication.run(MainApplication.class,args);
    }
}
第三步建立目录包结构

springboot整合hiberate_User_02


*properties文件语法是key=value的形式,注释加#;此外还有.yaml文件,对应yml文件语法,yml语法比xml语法更简洁。yml语法,下文thymeleaf的application配置

  1. controller
    服务定位 @Controller,@RequestMapping(“/url”)
    信息传递 Model,@ModelAttribute
    (๑•̀ㅂ•́)و✧ Model相关(作用+用法)
  2. dao
  3. service

1.2 集成Thymeleaf,Result结果封装

依赖导入—application配置—html名称空间引入
//thymeleaf依赖
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
<!DOCTYPE html>
<html  xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">   <!--引入thymeleaf名称空间-->
    <meta charset="UTF-8"> 
    <title>hello</title>
</head>
<body>

<div th:text = "${modelname}">离线数据</div>   
<!--th:text,thymeleaf文本替换  $里不允许有汉字  -->
<!--<p th:text = "${model的name}"></p>,这里可以有汉字-->

</body>
</html>

result包下,新建CodeMsg类和Result类,Result属性包括错误码信息CodeMsg(code + msg)、泛型类型data。如下分别,构造函数-属性-方法。
(?为啥要这样定义,每种方法存在的意义,换种写法可行不)——
Result是一个总的结果,msg+code是用于错误输出,对应error方法,而data主要是成功之后返回成功的数据data,对应success方法

public class Result<T> {
    private int code;
    private String msg;
    private T data;
    private Result(T data) {
        this.data = data;
    }
    private Result(int code,String msg){
        this.code = code;
        this.msg = msg;
    }
    private Result(CodeMsg codeMsg){
        if (codeMsg != null){
            this.code = codeMsg.getCode();
            this.msg = codeMsg.getMsg();
        }
    }
    public int getCode(){
        return code;
    }
    public void setCode(int code){
        this.code = code;
    }
    public String getMsg() {
        return msg;
    }
    public void setMsg(String msg) { 
    	this.msg = msg;
    }
    public T getData() {
        return data;
    }
    public void setData(T data) {
        this.data = data;
    }

    /**
     * 成功时调用
     */
    public static <T> Result<T> success(T data){
        return new Result<T>(data);
    }
    /**
     * 失败
     */
    public static <T> Result<T> error(CodeMsg codeMsg){
        return new Result<T>(codeMsg);
    }
}
public class CodeMsg {
    private int code;
    private String msg;

    //通用的错误码
    public static CodeMsg SUCCESS = new CodeMsg(0, "success");
    public static CodeMsg SERVER_ERROR = new CodeMsg(500100, "服务端异常");
    public static CodeMsg BIND_ERROR = new CodeMsg(500101, "参数校验异常:%s");
    public static CodeMsg REQUEST_ILLEGER = new CodeMsg(500102, "请求非法");
    public static CodeMsg ACCESS_LIMIT_REACHED = new CodeMsg(500102, "访问频繁");
    //登录模块 5002XX
    public static CodeMsg SESSION_ERROR = new CodeMsg(500210, "Session不存在或者已经失效");
    public static CodeMsg PASSWORD_EMPTY = new CodeMsg(500211, "登录密码不能为空");
    public static CodeMsg MOBILE_EMPTY = new CodeMsg(500212, "手机号不能为空");
    public static CodeMsg MOBILE_ERROR = new CodeMsg(500213, "手机号格式错误");
    public static CodeMsg MOBILE_NOT_EXIST = new CodeMsg(500214, "手机号不存在");
    public static CodeMsg PASSWORD_ERROR = new CodeMsg(500215, "密码错误");

    //商品模块 5003XX

    //订单模块 5004XX
    public static CodeMsg ORDER_NOT_EXIST = new CodeMsg(500400, "订单不存在");

    //秒杀模块 5005XX
    public static CodeMsg MIAO_SHA_OVER = new CodeMsg(500500, "商品已经秒杀完毕");
    public static CodeMsg REPEATE_MIAOSHA = new CodeMsg(500501, "不能重复秒杀");
    public static CodeMsg MIAOSHA_FAIL = new CodeMsg(500502, "秒杀失败");

    private CodeMsg(int i, String msg) {
        this.code = i;
        this.msg = msg;
    }
    private CodeMsg(){}

    public int getCode() {
        return code;
    }
    public void setCode(int code) {
        this.code = code;
    }
    public String getMsg() {
        return msg;
    }
    public void setMsg(String msg) {
        this.msg = msg;
    }

    public CodeMsg fillArgs(Object... args){  //可变参数,Object数组
        int code = this.code;
        String message = String.format(this.msg,args);//msg有参数 %s "参数校验异常:%s"
        return new CodeMsg(code,message);
    }

//    public static void main(String[] args) {
//        String out1 = String.format("format结果: %s%s%s","厦门","福州","泉州");
//        System.out.println(out1);  //format结果: 厦门福州泉州
//    }

    @Override
    public String toString() {
        return "CodeMsg{" +
                "code=" + code +
                ", msg='" + msg + '\'' +
                '}';
    }
}

1.3 Mybatis+Druid

导入依赖
<dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.2.6</version>
    </dependency>
    <dependency>
      <groupId>org.mybatis.spring.boot</groupId>
      <artifactId>mybatis-spring-boot-starter</artifactId>
      <version>2.2.0</version>
    </dependency>

mybatis+springboot官网

配置application
#mybatis
mybatis.type-aliases-package=com.imooc.miaosha.domain
mybatis.configuration.map-underscore-to-camel-case=true
mybatis.configuration.default-fetch-size=100
mybatis.configuration.default-statement-timeout=3000
mybatis.mapperLocations = classpath:com/imooc/miaosha/dao/*.xml

#druid
spring.datasource.url=jdbc:mysql://服务器或主机ip(ipv4):端口号(3306)/miaosha?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false
spring.datasource.username=**(改自己的)
spring.datasource.password=**
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource

(感觉阿里云服务器不是很稳定,还是我自己的操作失误?安全组都设置好了,ping不通的时候,解决办法 重启 (╥╯^╰╥))

MySQL与类的对应关系

domain包下的类和数据库中的表是一一对应的

domain包下的类

springboot整合hiberate_User_03


navicat连接到mysql,查询的表

springboot整合hiberate_spring_04


以user表(name,id)为例,有了domain包下的User类,在dao包下建立对应的IUserDao接口,在service包下建立UserService类实现接口方法,在controller包进行具体的操作。代码如下。

package com.imooc.miaosha.domain;

public class User {
    private int id;
    private String name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
package com.imooc.miaosha.dao;
@Mapper
public interface IUserDao {

    @Select("SELECT * FROM user WHERE id = #{id}")
    public User getById(@Param("id") int id);

    @Insert("insert into user values (#{id},#{name})")
    public int addUser(User user);
}
package com.imooc.miaosha.service;
@Service
public class UserService {
    @Autowired
    IUserDao userDao;
    public User getById(int id){
        return userDao.getById(id);
    }

    @Transactional    //不加该注解,id=5的数据会成功插入不会回滚
    public boolean tx(){
        User u1 = new User();
        u1.setId(5);
        u1.setName("1111");
        userDao.addUser(u1);

        User u2 = new User();
        u2.setId(2);
        u2.setName("222222");
        userDao.addUser(u2);
        return true;
    }
}
package com.imooc.miaosha.controller;
@Controller
@RequestMapping("/demo")
public class DemoController {
    @Autowired
    UserService userService;
    //测试访问数据库
    @RequestMapping("/db/get")
    @ResponseBody
    public Result<User> dbGet(){
        User user = userService.getById(1);
        return Result.success(user);
    }

    @RequestMapping("/db/tx")
    public Result<Boolean> dbTx(){
        userService.tx();
        return Result.success(true);
    }
}

1.4Jedis+Redis+通用缓存key

添加依赖+添加配置(application)

序列化用fastjson,把java对象转化成json字符串,写入redis中。为什么用fastjson而不用转化速度最快,效率最高的谷歌的Protocol Buffer ?因为Protocol Buffer 序列化之后是二进制的不方便直接读的文件,fastjson序列化后明文可读,方便查看,所以用fastjson。他们速度的区别,Protocol Buffer 1ms,Fastjson 2ms。

<dependency>
      <groupId>redis.clients</groupId>
      <artifactId>jedis</artifactId>
    </dependency>
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.38</version>
    </dependency>
#Redis
redis.host=*
redis.port=6379
redis.timeout=10
redis.password=*
redis.poolMaxTotal=1000  #连接池最大
redis.poolMaxIdle=1000  #最大空闲
redis.poolMaxWait=60000  #最大等待
借助Jedis连接处理redis数据

与application.properties 对应

@Component
@ConfigurationProperties(prefix = "redis")
public class RedisConfig {
    private String host;
    private int port;
    private int timeout; //秒
    private String password;
    private int poolMaxTotal;
    private int poolMaxIdle;
    private int poolMaxWait;//秒

获取JedisPool的工厂

@Service
public class RedisPoolFactory {

    @Autowired
    RedisConfig redisConfig;

    @Bean
    public JedisPool JedisPoolFactory(){
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setMaxIdle(redisConfig.getPoolMaxIdle());
        poolConfig.setMaxTotal(redisConfig.getPoolMaxTotal());
        poolConfig.setMaxWaitMillis(redisConfig.getPoolMaxWait()*1000); //这里设置的是毫秒值
        JedisPool jp = new JedisPool(poolConfig, redisConfig.getHost(), redisConfig.getPort(),
                redisConfig.getTimeout()*1000, redisConfig.getPassword(), 0);
        return jp;
    }
}

通过JedisPool获取Jedis对象,操作Redis数据库
为了区分不同用户设置的相同key,如用户A,set key1,用户B,set key1,在redis存取前,增加了一个前缀prefix,变为"A:key1",“B:key1”。

@Service
public class RedisService {
    @Autowired
    JedisPool jedisPool;

    /**
     * 获取单个对象
     * @param prefix
     * @param key
     * @param clazz
     * @param <T>
     * @return
     */
    public <T> T get(IKeyPrefix prefix,String key,Class<T> clazz){
        Jedis jedis = null;
        try{
            jedis = jedisPool.getResource();
            //生成真正的key
            String realKey = prefix.getPrefix() + key;
            String str = jedis.get(realKey);
            T t = stringToBean(str,clazz);   
            return t;
        }finally {
            returnToPool(jedis);
        }
    }

增加前缀,模板设计模式。
IKeyPrefix - KeyPrefix—(GoodsKey、UserKey、AccessKey、OrderKey、MiaoshaKey、MiaoshaUserKey)(这里key的设置并不跟数据表对应,根据需求设计)

package com.imooc.miaosha.redis;
public interface IKeyPrefix {
    public int expireSeconds();
    public String getPrefix();
}
package com.imooc.miaosha.redis;
public abstract class KeyPrefix implements IKeyPrefix {  //抽象类不能直接创建实例
//通过KeyPrefix进行各个模块区分,保证每个prefix,expireseconds都是不同的
    private int expireSeconds;
    private String prefix;
    public KeyPrefix(int expireSeconds,String prefix){
        this.expireSeconds = expireSeconds;
        this.prefix = prefix;
    }
    public KeyPrefix(String prefix){ // //默认0代表永不过期,public,可以被外面new,只是为了模拟方便,实际意义不大
        this(0,prefix);
    }
    @Override
    public int expireSeconds() {
        return expireSeconds;
    }

    @Override
    public String getPrefix() {
        String className = getClass().getSimpleName();
        return className+":"+prefix;
    }
}

springboot整合hiberate_redis_05

由Jedis+Redis联系JDBC和MySQL

JDBC是sun公司规定的Java与各种数据库连接的标准,由不同公司实现不同的驱动。使用步骤 如下

  1. 加载驱动
  2. 建立连接(DriverManager)
  3. 执行sql语句(statement、PrepareStatement(使用?占位符防止sql注入))
  4. 获得sql执行结果并封装结果集(ResultSet)
  5. 关闭资源、释放连接

一个数据库连接对象对应一个物理数据库连接,每次java对数据库进行操作时,都需要建立连接,使用完后关闭连接,频繁地打开、关闭连接造成系统性能低下,所以出现连接池技术,如C3P0、Druid。此外,Spring框架还重新封装了JDBC得到JDBCTemplate简化执行sql语句的步骤以及更方便的获取处理sql返回结果。

附:在linux上安装redis

在这里记录一下我自己服务器上各种软件安装目录
redis安装目录 :/www/server/redis
配置文件: /user/local/redis/redis.conf
日志文件: /user/local/redis/redis.log

redis-cli //进入redis命令行
auth 123456 //输入密码
? shutdown save //关闭并保存

redis安装使用的 linux命令记录

redis(6379)
tar -zvxf “name”.tar.gz //解压缩
mv “name” /新目录 //移动到新目录
cd /新目录
make -j 4 //编译
make install //将编译完成的文件添加到新目录中

redis.conf的参数修改
redis-server 启动
具体修改这俩参数:bind 0.0.0.0(整个网络)、daemonize yes //允许后台执行
修改密码的参数,requirepass 123456(密码)

命令 vi redis.conf //打开文件

命令 q //编辑文件

命令 :/string //查找文件内容

命令 :wq //保存文件退出

命令 ps - ef | grep redis //查看redis进程是否存在

ps -ef | grep详解

springboot整合hiberate_spring_06