SpringBoot 集成 Mybatis

0 条评论

跟着前面 SpringBoot 集成 Flyway 自动创建数据库表 之后,在原有的项目上继续集成 Mybatis 做数据库的访问、对象映射。

1. 继续添加 mybatis 的 spring boot starter 包

<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency>

这个依赖包主要引入了 mybatis-spring-boot-autoconfigure,利用了 spring boot 的 auto configure 机制,可以自动从 spring boot 里获取数据源,自动创建 SqlSessionFactory 等实例,所以有了它之后, mybatis 的什么 spring 配置都不用再管了。

2. 添加 Mapper 扫描的注解

这里使用注解的方式来使用 mybatis 的 mapper,由于没有 mybatis 配置文件了,就在 SprintBoot 应用的类上添加 @MapperScan 注解

@SpringBootApplication
@MapperScan("org.isouth.task")
public class TaskApp {
public static void main(String[] args) {
SpringApplication.run(TaskApp.class, args);
}
}

然后要求作为 Mybatis 的 Mapper 接口声明 @Mapper 注解:

@Mapper
public interface UserMapper {
@Select("SELECT * FROM USERS")
List<User> listUsers();

@Select("SELECT * FROM USERS WHERE EMAIL=#{email}")
User getUser(String email);

@Insert("INSERT INTO USERS (EMAIL, ALIAS) VALUES(#{email},#{alias})")
void addUser(User user);

@Delete("DELETE FROM USERS WHERE EMAIL=#{email}")
void deleteUser(String email);
}

3. 直接装配引用 Mapper接口

然后可以在 Controller 或者 Service 里直接使用 @Autowire 来装配 Mapper 接口,并直接调用接口方法使用了。

@Controller
@RequestMapping("/users")
public class UserController {
@Autowired
private UserMapper userMapper;

@RequestMapping(method = RequestMethod.POST, path = "")
@ResponseBody
public User addUser(@RequestBody User user) {
userMapper.addUser(user);
return user;
}

}

参考:

Windows10下搭建Mysql开发环境

0 条评论

这里的 Mysql 开发环境当然不是指开发 Mysql 程序本身, 只是开发使用 Mysql 的应用,准备个可用的 Mysql 而已。

自从 Windows 10 提供了 bash on ubuntu on windows 特性后,程序员搭建安装一些 linux(恩,知道mysql有windows版本)服务程序就特别方便了。

1. 首先确保 Windows 10 升级到了 Creators Update 或以上,并且启用 Bash on Windows 特性。(Store 里可以选择安装 Linux 发行版了,但是不知道是不是还叫做 bash on ubuntu on windows…)

2. 以安装的 ubuntu 为例, 启动 Bash on ubuntu on windows, 然后执行

sudo apt-get install mysql-server

中间会提示设置 root 密码,并执行

sudo service mysql start

就安装并启动好 mysql server了

3. 创建 database 和用户。

CREATE DATABASE todo CHARACTER SET utf8 COLLATE utf8_general_ci;

指定了字符集为 UTF8, 然后创建用户:

CREATE USER 'todo'@'localhost' identified by 'PASSWORD';

这里指定了用户名为 todo, 只允许通过 localhost 访问。

最后给新建用户赋上权限:

GRANT ALL PRIVILEGES ON todo.* TO todo@localhost;

SpringBoot 集成 Flyway 自动创建数据库表

1 条评论

使用 SpringBoot 开发 Java 应用时,如果涉及到数据库表的创建,可以集成 Flyway,在应用启动时,自动的初始化数据库表。

1. 首先当然是 Maven 工程引入 SpringBoot

参考 https://projects.spring.io/spring-boot/#quick-start 修改工程的 maven 配置,指定父工程,以及添加 spring-boot-starter-web 的依赖.

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.7.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>

2. 引入数据库模块的依赖

SpringBoot内建了大量功能的自动配置能力,这些配置能力大多基于运行时的类检测机制,所以需要引入 spring-jdbc 和 tomcat-jdbc, mysql-connector-java 几个依赖。spring-jdbc 为了启用数据源的自动配置,tomcat-jdbc 为了支持数据库连接池,mysql-connector-java 是添加 mysql 数据库的驱动,依赖如下:

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.11.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jdbc</artifactId>
<version>8.5.20</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.7-dmr</version>
</dependency>

3. 引入 flyway 的依赖

SpringBoot 的 JDBC AutoConfiguration 已经内建了对 flyway 的支持,唯一要做的,就是添加上 flyway-core 的依赖:

<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
<version>4.2.0</version>
</dependency>

4. 配置数据源

SpringBoot 的理念就是约定了大量的默认配置、自动装配能力。所以现在需要配置数据源不需要向以前一样配置各种 spring bean, 而只需要在 resources/application.properties (约定位置和名字,会自动加载)这个配置文件里写上数据库里连接的几个信息:

spring.datasource.url=jdbc:mysql://localhost:3306/todo?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.username=todo
spring.datasource.password=123456

是的,连 driver 信息都不用写,因为 springboot 会自动根据 url 信息去检测。

5. 提供数据库表初始化脚本

还是约定的机制和约定的配置,只需要在工程 resources/db/migration 这个目录下面以 V{version}__{name}.sql 的方式命名数据库脚本文件即可,version 和 name 当然是可变的,举个例子: V1.0__InitializeTables.sql, 注意版本和名字中间是两个下划线。

6. 启动应用自动建表

一切都OK了,确保数据库可连接,用户拥有足够的权限,就可以启动应用,SpringBoot 会自动配置数据源,并使用 Flyway 进行数据库迁移操作。

@SpringBootApplication
public class TaskApp {
public static void main(String[] args) {
SpringApplication.run(TaskApp.class, args);
}
}

参考:

HTTP Path 匹配和参数提取

2 条评论

给定一个 REST 的 Path 规则是: /orgs/<orgName>/apis/<apiName>/assets,其中尖括号包含的部分表示是一个可变的参数,参数一般情况下允许包含字母、数字、下划线。然后要求可以匹配符合规则的 HTTP URI Path 部分,并可以根据参数名称提取出参数的值来。

参考 bottle 项目的实现方案,将整个规则转换成一个正则表达式,然后利用 named group 特性,给定正则表达式捕获的分组名称来获取参数的值。

1. 把 /orgs/<orgName>/apis/<apiName>/assets 转换成 /orgs/(?:<orgName>[^/]+)/apis/(?:<apiName>[^/]+)/assets ,注意在 Java 7 后才支持这个正则表达式的写法,其中 (?:<orgName>[^/]+) 表示当前分组捕获结果可以用 orgName 来进行命名。为啥使用 named group 而不是直接按捕获分组顺序来查找,是因为情况再复杂一点的时候,正则表达式里的分组可能就很多很乱了,数也数不清顺序了。

2. 第一步转换成正则表达式的时候,同时把参数名称提取出来。

3. 正则表达式匹配,并根据参数名直接从正则表达式结果里获取参数

Java 代码样例:

  1. public static void main(String[] args) {
  2.     String rule = "/orgs/<orgName>/apis/<apiName>/assets";
  3.     String path = "/orgs/dev/apis/dts/assets";
  4.     Map params = match(rule, path);
  5.     System.out.println(params); // {apiName=dts, orgName=dev}
  6. }
  7. private static Map match(String rule, String path) {
  8.     StringBuilder pathRule = new StringBuilder();
  9.     List params = new ArrayList<>();
  10.     Pattern ruleSyntax = Pattern.compile("<(\\w*)>");
  11.     Matcher ruleMatcher = ruleSyntax.matcher(rule);
  12.     int offset = 0;
  13.     while (ruleMatcher.find()) {
  14.         int groupOffset = ruleMatcher.start();
  15.         pathRule.append(rule.substring(offset, groupOffset));
  16.         String groupName = ruleMatcher.group(1);
  17.         params.add(groupName);
  18.         // 拼接成 (?<name>[^/]+) 的正则表达式
  19.         pathRule.append("(?<").append(groupName).append(">[^/]+)");
  20.         offset = ruleMatcher.end();
  21.     }
  22.     if (offset < rule.length()) {
  23.         pathRule.append(rule.substring(offset, rule.length()));
  24.     }
  25.     Pattern pathPattern = Pattern.compile("^" + pathRule.toString() + "$");
  26.     Matcher pathMatcher = pathPattern.matcher(path);
  27.     Map result = new HashMap<>();
  28.     if (pathMatcher.matches()) {
  29.         for (String param : params) {
  30.             result.put(param, pathMatcher.group(param));
  31.         }
  32.     }
  33.     return result;
  34. }

Java 里 long 可以计数多久

0 条评论

long 作为 JVM 的 primitive 类型之一,是 64bit 长的有符号数字,它的大小范围是 -9223372036854775808 到 9223372036854775807 (-263 to 263- 1),而我们经常使 long (比如 AtomicLong 对象)用来计数,那么现在可以算算,在我们的使用场景下,long 可以使用多久就会达到最大值呢?

假如我们的使用场景中,每一秒使用计数器计数 10w 次,并且不间断保持计数,那么一天会计数 10w*60*60*24=8,640,000,000, 也就是约 86 亿次, 这个数字看起来很大了,我们用 long 的最大值来除以试试

9223372036854775807/8640000000=1067519911.6730064591435185185185

也就是大概可以不间断地计数 10.6 亿天,换算成年呢?

1067519911.6730064591435185185185/365=2924712.086775360162037037037037

也就是大概 292w 年,所以,如果计数器要求每天计数 86 亿次的情况下,long 可以支持我们的程序跑上几百万年,完全不用担心溢出的问题。