spring中使用mybatis/mybatis plus连接sqlserver

spring中使用mybatis/mybatis plus连接sqlserver

本文主要关注如何使用mybatis/mybatis plus连接SQL Server数据库,因此将省略其他项目配置、代码。

框架选择

应用框架:spring boot
ORM框架:mybatis plus(对于连接数据库而言,mybatis和mybatis plus其实都一样)
数据库连接池:druid

pom依赖

此处仅给出我的配置,mybatis/druid请依据自己项目的需要进行选择。
方便起见我用的是mybatis plus

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<!--mybatis plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.1.0</version>
</dependency>

<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.7</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.0</version>
</dependency>

<!-- druid 连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>

<!--for SqlServer-->
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>sqljdbc4</artifactId>
<version>4.0</version>
</dependency>

配置数据源

添加数据库配置

YAML文件中添加自己数据库的地址

1
2
3
4
5
# SQL Server数据库
spring.datasource.xx.url: jdbc:sqlserver://你的数据库地址:1433;databaseName=你的数据库名称
spring.datasource.xx.username: xxxx
spring.datasource.xx.password: xxxx
spring.datasource.xx.driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver

添加数据源

此处和平时我们在spring boot中集成mybatis/mybatis plus一样,添加bean即可。
由于平时经常用到多个数据库,此处展示一个多数据源的例子:一个是mysql,一个是SQL Server
有关mybatis plus配置数据源的注意事项,比如配置mapper文件夹等,请自行问度娘,此处不再一一指出。
注意:下面代码来自实际代码,但批量删除了敏感信息、重新命名,因而可能存在与前面配置信息不一致的地方,仅仅是一个示例

Mysql数据源

mysql数据源配置,注意,由于是多数据源,需要有一个数据源配置中加上@Primary注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
@Configuration
@MapperScan(basePackages = "com.xxx.mapper", sqlSessionFactoryRef = "mysqlSqlSessionFactory")
public class MySQLMybatisPlusConfig {

@Autowired
private MybatisPlusProperties properties;

@Autowired
private ResourceLoader resourceLoader = new DefaultResourceLoader();

@Autowired(required = false)
private Interceptor[] interceptors;

@Autowired(required = false)
private DatabaseIdProvider databaseIdProvider;

@Autowired
private Environment env;

@Bean(name = "mysqlDataSource")
@Primary
public DataSource getRecruitDataSource() throws Exception {
Properties props = new Properties();
props.put("driverClassName", env.getProperty("spring.datasource.mysqlData.driver-class-name"));
props.put("url", env.getProperty("spring.datasource.mysqlData.url"));
props.put("username", env.getProperty("spring.datasource.mysqlData.username"));
props.put("password", env.getProperty("spring.datasource.mysqlData.password"));
return DruidDataSourceFactory.createDataSource(props);
}

/**
* mybatis-plus分页插件
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor page = new PaginationInterceptor();
page.setDialectType("mysql");
return page;
}

@Bean(name = "mysqlSqlSessionFactory")
@Primary
public MybatisSqlSessionFactoryBean mybatisSqlSessionFactoryBean(@Qualifier("mysqlDataSource") DataSource mysqlDataSource) throws IOException {
MybatisSqlSessionFactoryBean mybatisPlus = new MybatisSqlSessionFactoryBean();
try {
mybatisPlus.setDataSource(mysqlDataSource);
} catch (Exception e) {
e.printStackTrace();
}
mybatisPlus.setVfs(SpringBootVFS.class);
// 设置分页插件
MybatisConfiguration mc = new MybatisConfiguration();
mc.setDefaultScriptingLanguage(MybatisXMLLanguageDriver.class);
mc.setMapUnderscoreToCamelCase(true);// 数据库和java都是驼峰,就不需要
mybatisPlus.setConfiguration(mc);
if (this.databaseIdProvider != null) {
mybatisPlus.setDatabaseIdProvider(this.databaseIdProvider);
}
mybatisPlus.setTypeAliasesPackage("com.xxx.mysql.bean.model");
mybatisPlus.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
mybatisPlus.setMapperLocations(this.properties.resolveMapperLocations());
// 设置mapper.xml文件的路径
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resource = resolver.getResources("classpath:mapper/*.xml");
mybatisPlus.setMapperLocations(resource);

return mybatisPlus;
}
}

SQL Server数据源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
@Configuration
@MapperScan(basePackages = "com.xxx.survey.mapper", sqlSessionFactoryRef = "xxSqlSessionFactory")
public class SqlServerMybatisConfig {

@Autowired
private MybatisPlusProperties properties;

@Autowired
private ResourceLoader resourceLoader = new DefaultResourceLoader();

@Autowired(required = false)
private Interceptor[] interceptors;

@Autowired(required = false)
private DatabaseIdProvider databaseIdProvider;

@Autowired
private Environment env;

@Bean(name = "xxDataSource")
public DataSource getAttendanceDataSource() throws Exception {
Properties props = new Properties();
props.put("driverClassName", env.getProperty("spring.datasource.xx.driver-class-name"));
props.put("url", env.getProperty("spring.datasource.xx.url"));
props.put("username", env.getProperty("spring.datasource.xx.username"));
props.put("password", env.getProperty("spring.datasource.xx.password"));
return DruidDataSourceFactory.createDataSource(props);
}


@Bean(name = "xxSqlSessionFactory")
public MybatisSqlSessionFactoryBean mybatisSqlSessionFactoryBean(@Qualifier("xxDataSource") DataSource xxDataSource) throws IOException {
MybatisSqlSessionFactoryBean mybatisPlus = new MybatisSqlSessionFactoryBean();
try {
mybatisPlus.setDataSource(xxDataSource);
} catch (Exception e) {
e.printStackTrace();
}
mybatisPlus.setVfs(SpringBootVFS.class);
// 设置分页插件
MybatisConfiguration mc = new MybatisConfiguration();
mc.setDefaultScriptingLanguage(MybatisXMLLanguageDriver.class);
mc.setMapUnderscoreToCamelCase(true);// 数据库和java都是驼峰,就不需要
mybatisPlus.setConfiguration(mc);
if (this.databaseIdProvider != null) {
mybatisPlus.setDatabaseIdProvider(this.databaseIdProvider);
}
mybatisPlus.setTypeAliasesPackage("com.xxx.survey.bean.model");
mybatisPlus.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
mybatisPlus.setMapperLocations(this.properties.resolveMapperLocations());
// 设置mapper.xml文件的路径
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resource = resolver.getResources("classpath:mapper/*.xml");
mybatisPlus.setMapperLocations(resource);

return mybatisPlus;
}
}

生成ORM代码

到这里,程序启动应该没什么问题,接着就应该生成DAO层、Service层代码了
mybatis和mybatis plus在此处按照和连接mysql时一样的方法,根据需要写代码即可。
比如对于mybatis plus,需要写3处代码:

  1. 实体bean,可以利用Spring Boot Code Generator!来根据SQL表结构自动生成
  2. Mapper代码:都有模板,mybatis plus自己封装的方法已经很够用,有单独需求可以自己写xml来自定义SQL
1
2
3
4
@Mapper
public interface XXXMapper extends BaseMapper<XXX> {

}
  1. Service代码
    好像也有现成的工具可以自动生成mapper service代码来着。
    Service接口
1
2
3
public interface XXXService extends IService<XXX> {
}

ServiceImpl

1
2
3
4
5
@Service
public class XXXServiceImpl extends ServiceImpl<XXXMapper, XXX>
implements XXXService {

}

参考资料

mysql中判断字段是否包含数字或者是否为纯数字

mysql中判断字段是否包含数字或者是否为纯数字

各种场景

判断字段是否包含数字

1
select name from text where name regex '[0-9]'

使用like模糊查询包含某个数字

1
select * from text where name like '%1%'

可能会筛出各种不适我们想要的,比如包含“10”的字段也会筛选出。

使用mysql原生函数FIND_IN_SET查询包含某个数字

1
select * from text where find_in_set(1,name)

比like更精确一下。

使用regexp正则匹配纯数字

1
select * from text where (name REGEXP '[^0-9.]')=0;

例如:
当 SELECT “666” REGEXP ‘[^0-9.]’ 结果为 0 ,代表为纯数字
当 SELECT “6hhhhh” REGEXP ‘[^0-9.]’ 时, 结果为1 ,代表不为纯数字

使用regexp正则匹配字段值不包含数字

1
select * from text where name NOT REGEXP '[0-9]+';

参考资料

mybatis常用写法-mapper xml传入多个参数

mybatis常用写法-mapper xml传入多个参数

mapper xml文件中:

1
2
3
4
5
6
7
8
9
10
<resultMap id="XxxResultMap" type="com.xxx.xxxx">
<id column="id" property="id" jdbcType="INTEGER" />
...
</resultMap>

<select id="selectXXXX" resultMap="XxxResultMap">
SELECT id, title, type, release_id , ...
FROM test
WHERE release_id = ${id} and type = ${type}
</select>  

mapper接口中:

1
xxx selectXXXX(@Param(value = "id") String id,@Param(value = "type") String type);

参考资料

mysql中使用replace regexp实现正则替换

mysql中使用replace regexp实现正则替换

mysql的正则匹配用regexp,而替换字符串用REPLACE(str,from_str,to_str)

例如
UPDATE myTable SET HTML=REPLACE(HTML,'<br>','') WHERE HTML REGEXP '(<br */*>\s*){2,}'

更多例子如下:

为了找出以“d”开头的名字,使用“^”匹配名字的开始:

SELECT * FROM master_data.md_employee WHERE name REGEXP ‘^d’;

为了找出以“love”结尾的名字,使用“$”匹配名字的结尾:

SELECT id,name FROM master_data.md_employee WHERE name REGEXP ‘love$’;

为了找出包含一个“w”的名字,使用以下查询:

SELECT id,name FROM master_data.md_employee WHERE name REGEXP ‘w’;

为了找出包含正好5个字符的名字,使用“^”和“$”匹配名字的开始和结尾,和5个“.”实例在两者之间:

SELECT id,name FROM master_data.md_employee WHERE name REGEXP ‘^…..$’;

或者:

SELECT id,name FROM master_data.md_employee WHERE name REGEXP ‘^.{5}$’;

参考资料:

MySQL中使用replace、regexp进行正则表达式替换的用法分析

MySQL如何实现正则查找替换?

linux批量删除多个文件,正则匹配删除文件

linux批量删除多个文件,正则匹配删除文件

一般的删除文件的操作

  • 删除几个文件 rm 文件1 文件2
  • 删除某些固定字母开头的文件 rm xxx*
  • 删除文件夹下面所有文件 rm * -rf
  • 删除一类文件 rm *.txt

先用找到想删除的文件,再执行删除操作

find命令找到指定文件

首先查找要删除的某类批量的文件:
find . -maxdepth 1 -regex ".*ws.*"
maxdepth参数为1表示只在当前目录查找,不递归查找子目录
regex参数是正则表达式
上面的命令表示查找所有文件名中含有“ws”的文件。
find . -regex “.*.txt|shtxt∥sh”
加参数“-regextype type”可以指定“type”类型的正则语法,find支持的正则语法有:valid types are findutils-default',awk,egrep,ed,emacs,gnu-awk,grep,posix-awk,posix-basic,posix-egrep,posix-extended,posix-minimal-basic,sed` .

显示20分钟前的文件
find /home/prestat/bills/test -type f -mmin +20 -exec ls -l {} \;

find /home/prestat/bills/test -type f -mmin +20 -exec ls -l {} +

删除20分钟前的文件
find /home/prestat/bills/test -type f -mmin +20 -exec rm {} \;

显示20天前的目录
find /home/prestat/bills/test -type d -mtime +20 -exec ls -l {} \;

删除20天前的目录
find /home/prestat/bills/test -type d -mtime +20 -exec rm {} \;

在20-50天内修改过的文件
find ./ -mtime +20 -a -mtime -50 -type f

排除某些目录:
find ${JENKINS_HOME}/jobs -maxdepth 1 -name "*" -mtime +60 ! -path /var/lib/jenkins/jobs | xargs ls -ld;

排除某些文件:
find ${JENKINS_HOME}/jobs -maxdepth 1 ! -name "*.xml" -mtime +60 ! -path /var/lib/jenkins/jobs | xargs ls -ld;

xargs rm执行删除操作

批量删除上面查找到的文件:
find . -maxdepth 1 -regex ".*ws.*" | xargs rm -rf
xargs是把前面的输出作为后面的参数,如果多行输出,就多次执行后面的命令

有的linux系统支持的regex正则表达式不一样,可以使用下面的方式替换
find . -maxdepth 1 -name "*.c" | xargs rm -rf

还有使用下面的命令也可以:
find . -maxdepth 1 -regex ".*ws.*" -exec rm -rf {} \;

参考资料

spring框架中获取客户端的真实ip

spring框架中获取客户端的真实ip

题外话,前端也可以调用已有的接口获取ip,例如调用搜狐接口 https://pv.sohu.com/cityjson?ie=utf-8

在spring框架中,获取IP接口,则需要获取 HttpServletRequest 对象,该对象中包含了客户端请求的相关信息。

java代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/** 
* @Description: 获取客户端IP地址
*/
private String getIpAddr(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
if(ip.equals("127.0.0.1")){
//根据网卡取本机配置的IP
InetAddress inet=null;
try {
inet = InetAddress.getLocalHost();
} catch (Exception e) {
e.printStackTrace();
}
ip= inet.getHostAddress();
}
}
// 多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
if(ip != null && ip.length() > 15){
if(ip.indexOf(",")>0){
ip = ip.substring(0,ip.indexOf(","));
}
}
return ip;
}

注意,此处获取的第一个HTTP header字段x-forwarded-for,可能被伪造,具体参见这篇文章:HTTP 请求头中的 X-Forwarded-For,X-Real-IP

对上述代码做改造,即可得到如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/** 
* @Description: 获取客户端IP地址
*/
private String getIpAddr(HttpServletRequest request) {
String ip = request.getHeader("x-Real-IP");
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
if(ip.equals("127.0.0.1")){
//根据网卡取本机配置的IP
InetAddress inet=null;
try {
inet = InetAddress.getLocalHost();
} catch (Exception e) {
e.printStackTrace();
}
ip= inet.getHostAddress();
}
}
// 多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
if(ip != null && ip.length() > 15){
if(ip.indexOf(",")>0){
ip = ip.substring(0,ip.indexOf(","));
}
}
return ip;
}

参考资料

Mybatis-Plus select不列出全部字段,只查询部分字段

Mybatis-Plus select不列出全部字段,只查询部分字段

mybatis-plus select查询语句默认是查全部字段,有两种方法可以指定要查询的字段

假定表结构如下:

1
2
3
4
5
6
7
8
CREATE TABLE `user` (
`id` bigint(20) NOT NULL COMMENT '主键',
`name` varchar(30) DEFAULT NULL COMMENT '姓名',
`age` int(11) DEFAULT NULL COMMENT '年龄',
`email` varchar(50) DEFAULT NULL COMMENT '邮箱',
`manager_id` bigint(20) DEFAULT NULL COMMENT '直属上级id',
`create_time` datetime DEFAULT NULL COMMENT '创建时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

假设目前仅需要查询name,age两个字段。

方法1:只需要查询出name和age两个字段:使用queryWrapper的select()方法指定要查询的字段

1
2
3
4
5
6
7
@Test
public void selectByWrapper1() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.select("name", "age").like("name", "雨");
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}

方法2:查询出除manager_id和create_time外其它所有字段的数据:同样使用queryWrapper的select()方法

1
2
3
4
5
6
7
8
@Test
public void selectByWrapper2() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.select(User.class, info -> !info.getColumn().equals("manager_id")
&& !info.getColumn().equals("create_time"));
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}

参考资料

mybatis_plus常见用法-不用xml实现自定义查询

mybatis_plus常见用法-不用xml实现自定义查询

找到两种方法:
1、采用mybatis注解的方式
参见:MyBatis Plus 自定义查询语句
DAO层:

1
2
3
4
5
6
7
8
9
10
11
@Select("select b.bomName, " +
"b.bomProductType, b.bomMaterial, " +
"o.customerID AS bomID, " +
"o.ordersDataNo AS qrCode, " +
"s.deliveryDate AS barCode, " +
"s.mainType AS workshop " +
"FROM mes_order_bom b " +
"LEFT JOIN mes_order_ordersdata o ON b.ordersID = o.id " +
"LEFT JOIN mes_order_soncontract s ON o.sonContractID = s.id " +
"WHERE o.ordersDataNo IN (#{orderNoList})")
List<MesOrderBom> getBomAndOrderCodeNumber(@Param("orderNoList")List<String> orderNoList);

Service层:

1
List<MesOrderBom> getBomAndOrderCodeNumber(List<String> orderNoList);

Service实现类:

1
2
3
4
@Override
public List<MesOrderBom> getBomAndOrderCodeNumber(List<String> orderNoList) {
return this.baseMapper.getBomAndOrderCodeNumber(orderNoList);
}

2、自定义实现
结合mybatis-plus 实现无XML多表联查询
项目地址:multipleselect
java 结合mybatis-plus 实现非手写sql多表查询

参考资料

Java中日期格式化yyyyMMdd和YYYYMMdd的区别

Java中日期格式化yyyyMMdd和YYYYMMdd的区别

Java中日期格式化yyyyMMdd和YYYYMMdd的区别

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public static void main(String[] args) {
//YYYY 是表示:当天所在的周属于的年份,一周从周日开始,周六结束,只要本周跨年,那么这周就算入下一年。
//2019-12-29至2020-1-4跨年周
Calendar calendar = Calendar.getInstance();
//2019-12-28
calendar.set(2019, Calendar.DECEMBER, 28);
Date strDate1 = calendar.getTime();
//2019-12-29
calendar.set(2019, Calendar.DECEMBER, 29);
Date strDate2 = calendar.getTime();
// 2019-12-31
calendar.set(2019, Calendar.DECEMBER, 31);
Date strDate3 = calendar.getTime();
// 2020-01-01
calendar.set(2020, Calendar.JANUARY, 1);
Date strDate4 = calendar.getTime();

DateFormat df1 = new SimpleDateFormat("yyyyMMdd");
DateFormat df2 = new SimpleDateFormat("YYYYMMdd");
//yyyyMMdd
System.out.println("yyyyMMdd");
System.out.println("2019-12-28: " + df1.format(strDate1));
System.out.println("2019-12-29: " + df1.format(strDate2));
System.out.println("2019-12-31: " + df1.format(strDate3));
System.out.println("2020-01-01: " + df1.format(strDate4));
//YYYYMMdd
System.out.println("YYYYMMdd");
System.out.println("2019-12-28: " + df2.format(strDate1));
System.out.println("2019-12-29: " + df2.format(strDate2));
System.out.println("2019-12-31: " + df2.format(strDate3));
System.out.println("2020-01-01: " + df2.format(strDate4));
}

输出结果:

1
2
3
4
5
6
7
8
9
10
yyyyMMdd
2019-12-28: 20191228
2019-12-29: 20191229
2019-12-31: 20191231
2020-01-01: 20200101
YYYYMMdd
2019-12-28: 20191228
2019-12-29: 20201229
2019-12-31: 20201231
2020-01-01: 20200101

原因:
YYYY是week-based-year,表示:当天所在的周属于的年份,一周从周日开始,周六结束,只要本周跨年,那么这周就算入下一年。所以2019年12月31日那天在这种表述方式下就已经 2020 年了。而当使用yyyy的时候,就还是 2019 年。

相关说明:
https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html#patterns
https://nakedsecurity.sophos.com/2019/12/23/serious-security-the-decade-ending-y2k-bug-that-wasnt/

mysql 常用函数整理-获取多个字段最大值最小值

mysql 常用函数整理-获取多个字段最大值最小值

内容转自:https://blog.csdn.net/yangshijin1988/article/details/51698061/

greatest(字段1,字段2,字段3,..,字段n)  取最大值

least(字段1,字段2,字段3,…,字段n)   取最小值

示例:

SELECT GREATEST(2,3,4);   结果:4
SELECT LEAST(2,3,4);   结果:2

SELECT GREATEST(DATE(‘2016-05-02’), DATE(‘2015-05-02’), DATE(‘2017-05-02’));   结果:2017-05-02
SELECT LEAST(DATE(‘2016-05-02’), DATE(‘2015-05-02’), DATE(‘2017-05-02’));   结果:2015-05-02