├── src
├── main
│ ├── resources
│ │ ├── generator
│ │ │ ├── config.properties
│ │ │ └── generatorConfig.xml
│ │ ├── mapper
│ │ │ └── User.xml
│ │ ├── sql
│ │ │ └── test1.sql
│ │ ├── logback.xml
│ │ └── application.yml
│ └── java
│ │ └── com
│ │ └── zx
│ │ └── springmybatis
│ │ ├── config
│ │ ├── mybatis
│ │ │ └── CommonMapper.java
│ │ ├── druid
│ │ │ ├── DruidStatFilter.java
│ │ │ ├── DruidDataSourceConfiguration.java
│ │ │ └── DruidStatViewServlet.java
│ │ └── cache
│ │ │ ├── CustomRedisCacheExpireProperties.java
│ │ │ └── RedisCacheConfig.java
│ │ ├── service
│ │ ├── UserService.java
│ │ └── impl
│ │ │ ├── UserServiceImpl.java
│ │ │ └── CacheService.java
│ │ ├── dto
│ │ └── GradeDTO.java
│ │ ├── entity
│ │ ├── Grade.java
│ │ └── User.java
│ │ ├── dao
│ │ ├── UserMapper.java
│ │ ├── GradeMapper.java
│ │ └── driver
│ │ │ └── CommonConditionLanguageDriver.java
│ │ ├── SpringMyBatisApplication.java
│ │ └── controller
│ │ └── TestController.java
└── test
│ └── java
│ └── com
│ └── zx
│ └── springmybatis
│ ├── SpringMyBatisApplicationTests.java
│ ├── service
│ └── impl
│ │ └── CacheServiceTest.java
│ └── dao
│ └── UserMapperTest.java
├── .gitignore
├── pom.xml
└── README.md
/src/main/resources/generator/config.properties:
--------------------------------------------------------------------------------
1 | # 数据库配置
2 | jdbc.driverClass = com.mysql.jdbc.Driver
3 | jdbc.url = jdbc:mysql://localhost:3306/sms-sender
4 | jdbc.user = root
5 | jdbc.password = 970389
6 | # 通用Mapper配置
7 | mapper.plugin = tk.mybatis.mapper.generator.MapperPlugin
8 | mapper.Mapper = com.zx.springmybatis.config.mybatis.CommonMapper
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | !.mvn/wrapper/maven-wrapper.jar
3 |
4 | ### STS ###
5 | .apt_generated
6 | .classpath
7 | .factorypath
8 | .project
9 | .settings
10 | .springBeans
11 |
12 | ### IntelliJ IDEA ###
13 | .idea
14 | *.iws
15 | *.iml
16 | *.ipr
17 |
18 | ### NetBeans ###
19 | nbproject/private/
20 | build/
21 | nbbuild/
22 | dist/
23 | nbdist/
24 | .nb-gradle/
--------------------------------------------------------------------------------
/src/main/resources/mapper/User.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
--------------------------------------------------------------------------------
/src/main/java/com/zx/springmybatis/config/mybatis/CommonMapper.java:
--------------------------------------------------------------------------------
1 | package com.zx.springmybatis.config.mybatis;
2 |
3 | import tk.mybatis.mapper.common.Mapper;
4 | import tk.mybatis.mapper.common.MySqlMapper;
5 |
6 | /**
7 | * author:ZhengXing
8 | * datetime:2017/11/16 0016 15:16
9 | * 通用Mapper顶层接口,所有mapper需要继承他
10 | */
11 | public interface CommonMapper extends Mapper,MySqlMapper {
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/com/zx/springmybatis/service/UserService.java:
--------------------------------------------------------------------------------
1 | package com.zx.springmybatis.service;
2 |
3 | import com.github.pagehelper.PageInfo;
4 | import com.zx.springmybatis.entity.User;
5 |
6 | import java.util.List;
7 |
8 | /**
9 | * author:Administrator
10 | * datetime:2017/11/7 0007 10:13
11 | */
12 | public interface UserService {
13 | List getAll();
14 |
15 | PageInfo getAllForPage(Integer pageNum, Integer pageSize);
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/src/test/java/com/zx/springmybatis/SpringMyBatisApplicationTests.java:
--------------------------------------------------------------------------------
1 | package com.zx.springmybatis;
2 |
3 | import org.junit.Test;
4 | import org.junit.runner.RunWith;
5 | import org.springframework.boot.test.context.SpringBootTest;
6 | import org.springframework.test.context.junit4.SpringRunner;
7 |
8 | @RunWith(SpringRunner.class)
9 | @SpringBootTest
10 | public class SpringMyBatisApplicationTests {
11 |
12 | @Test
13 | public void contextLoads() {
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/com/zx/springmybatis/config/druid/DruidStatFilter.java:
--------------------------------------------------------------------------------
1 | package com.zx.springmybatis.config.druid;
2 |
3 | import com.alibaba.druid.support.http.WebStatFilter;
4 |
5 | import javax.servlet.annotation.WebFilter;
6 | import javax.servlet.annotation.WebInitParam;
7 |
8 | /**
9 | * author:Administrator
10 | * datetime:2017/11/7 0007 10:10
11 | * druid 过滤器
12 | */
13 | //@WebFilter(filterName="druidWebStatFilter",urlPatterns="/*",
14 | // initParams={
15 | // @WebInitParam(name="exclusions",value="*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*")// 忽略资源
16 | // })
17 | public class DruidStatFilter extends WebStatFilter{
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/com/zx/springmybatis/dto/GradeDTO.java:
--------------------------------------------------------------------------------
1 | package com.zx.springmybatis.dto;
2 |
3 | import com.zx.springmybatis.entity.Grade;
4 | import com.zx.springmybatis.entity.User;
5 | import lombok.AllArgsConstructor;
6 | import lombok.Builder;
7 | import lombok.Data;
8 | import lombok.experimental.Accessors;
9 |
10 | import java.io.Serializable;
11 | import java.util.List;
12 |
13 | /**
14 | * author:ZhengXing
15 | * datetime:2017/11/29 0029 11:28
16 | * 班级信息和班级下所有学生
17 | */
18 | @Data
19 | @Builder
20 | @AllArgsConstructor
21 | @Accessors(chain = true)
22 | public class GradeDTO implements Serializable {
23 | private Grade grade;
24 | private List users;
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/com/zx/springmybatis/entity/Grade.java:
--------------------------------------------------------------------------------
1 | package com.zx.springmybatis.entity;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Builder;
5 | import lombok.Data;
6 | import lombok.NoArgsConstructor;
7 |
8 | import javax.persistence.GeneratedValue;
9 | import javax.persistence.Id;
10 | import javax.persistence.Table;
11 | import java.io.Serializable;
12 | import java.util.List;
13 |
14 | /**
15 | * author:ZhengXing
16 | * datetime:2017/11/28 0028 09:34
17 | * 班级
18 | */
19 | @Data
20 | @Builder
21 | @Table
22 | @NoArgsConstructor
23 | @AllArgsConstructor
24 | public class Grade implements Serializable{
25 | @Id
26 | @GeneratedValue(generator = "JDBC")
27 | private Long id;
28 | private String name;
29 |
30 | public Grade(String name) {
31 | this.name = name;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/com/zx/springmybatis/config/druid/DruidDataSourceConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.zx.springmybatis.config.druid;
2 |
3 | import com.alibaba.druid.pool.DruidDataSource;
4 | import org.springframework.boot.context.properties.ConfigurationProperties;
5 | import org.springframework.context.annotation.Bean;
6 | import org.springframework.context.annotation.Configuration;
7 |
8 | import javax.sql.DataSource;
9 |
10 | /**
11 | * author:Administrator
12 | * datetime:2017/11/7 0007 09:37
13 | * druid 数据源 配置类
14 | */
15 | //@Configuration
16 | public class DruidDataSourceConfiguration {
17 | @Bean
18 | @ConfigurationProperties(prefix = "spring.datasource")
19 | public DataSource druidDataSource() {
20 | DruidDataSource druidDataSource = new DruidDataSource();
21 | return druidDataSource;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/com/zx/springmybatis/dao/UserMapper.java:
--------------------------------------------------------------------------------
1 | package com.zx.springmybatis.dao;
2 |
3 | import com.zx.springmybatis.config.mybatis.CommonMapper;
4 | import com.zx.springmybatis.dao.driver.CommonConditionLanguageDriver;
5 | import com.zx.springmybatis.entity.User;
6 | import org.apache.ibatis.annotations.*;
7 | import org.springframework.stereotype.Repository;
8 |
9 | import java.util.List;
10 |
11 | /**
12 | * author:Administrator
13 | * datetime:2017/11/7 0007 10:12
14 | * 用户mapper
15 | */
16 | @Repository
17 | public interface UserMapper extends CommonMapper{
18 | // @Select("select * from user")
19 | // List getAll();
20 |
21 | User a(Long id);
22 |
23 | /**
24 | * 使用LanguageDriver进行通用的条件查询
25 | */
26 | @Lang(CommonConditionLanguageDriver.class)
27 | @Select("select * from user")
28 | List findByCondition(User user);
29 |
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/com/zx/springmybatis/entity/User.java:
--------------------------------------------------------------------------------
1 | package com.zx.springmybatis.entity;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Builder;
5 | import lombok.Data;
6 | import lombok.NoArgsConstructor;
7 | import lombok.experimental.Accessors;
8 |
9 | import javax.persistence.GeneratedValue;
10 | import javax.persistence.Id;
11 | import javax.persistence.Table;
12 | import java.io.Serializable;
13 | import java.util.List;
14 |
15 | /**
16 | * author:Administrator
17 | * datetime:2017/11/7 0007 09:10
18 | */
19 |
20 | @Data
21 | @Builder
22 | @Table
23 | @NoArgsConstructor
24 | @AllArgsConstructor
25 | @Accessors(chain = true)
26 | public class User implements Serializable{
27 | @Id
28 | @GeneratedValue(generator = "JDBC")
29 | private Long id;
30 | private String name;
31 | private String password;
32 | private String loginAddress;
33 | private Long gradeId;
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/java/com/zx/springmybatis/config/druid/DruidStatViewServlet.java:
--------------------------------------------------------------------------------
1 | package com.zx.springmybatis.config.druid;
2 |
3 | import com.alibaba.druid.support.http.StatViewServlet;
4 |
5 | import javax.servlet.annotation.WebInitParam;
6 | import javax.servlet.annotation.WebServlet;
7 |
8 | /**
9 | * author:Administrator
10 | * datetime:2017/11/7 0007 10:04
11 | * druid servlet配置
12 | */
13 | //@WebServlet(urlPatterns = "/druid/*",
14 | //initParams = {
15 | // @WebInitParam(name = "allow",value = ""),// IP白名单 (没有配置或者为空,则允许所有访问)
16 | // @WebInitParam(name = "deny",value = ""),// IP黑名单 (存在共同时,deny优先于allow)
17 | // @WebInitParam(name = "loginUsername",value = "zx"),//用户名
18 | // @WebInitParam(name = "loginPassword",value = "970389"),//密码
19 | // @WebInitParam(name = "resetEnable",value = "false")// 禁用HTML页面上的“Reset All”功能
20 | //})
21 | public class DruidStatViewServlet extends StatViewServlet{
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/com/zx/springmybatis/SpringMyBatisApplication.java:
--------------------------------------------------------------------------------
1 | package com.zx.springmybatis;
2 |
3 | import lombok.extern.slf4j.Slf4j;
4 | import org.mybatis.spring.annotation.MapperScan;
5 | import org.springframework.boot.SpringApplication;
6 | import org.springframework.boot.autoconfigure.SpringBootApplication;
7 | import org.springframework.boot.web.servlet.ServletComponentScan;
8 | import org.springframework.cache.annotation.EnableCaching;
9 |
10 | @SpringBootApplication
11 | @ServletComponentScan
12 | @MapperScan("com.zx.springmybatis.dao")
13 | @EnableCaching
14 | @Slf4j
15 | public class SpringMyBatisApplication {
16 |
17 | public static void main(String[] args) {
18 | SpringApplication.run(SpringMyBatisApplication.class, args);
19 | log.info("---------------------------------");
20 | log.warn("----------------------------------");
21 | log.debug("--------------------------------");
22 | log.error("------------------------------------");
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/com/zx/springmybatis/controller/TestController.java:
--------------------------------------------------------------------------------
1 | package com.zx.springmybatis.controller;
2 |
3 | import com.github.pagehelper.PageInfo;
4 | import com.zx.springmybatis.entity.User;
5 | import com.zx.springmybatis.service.UserService;
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.web.bind.annotation.GetMapping;
8 | import org.springframework.web.bind.annotation.RequestMapping;
9 | import org.springframework.web.bind.annotation.RestController;
10 |
11 | import java.util.List;
12 |
13 | /**
14 | * author:Administrator
15 | * datetime:2017/11/7 0007 10:16
16 | */
17 | @RestController
18 | @RequestMapping("/")
19 | public class TestController {
20 |
21 | @Autowired
22 | private UserService userService;
23 |
24 | @GetMapping("/getAll")
25 | public List getAll() {
26 | return userService.getAll();
27 | }
28 |
29 | @GetMapping("/page")
30 | public PageInfo page(Integer pageNum,Integer pageSize){
31 | return userService.getAllForPage(pageNum, pageSize);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/com/zx/springmybatis/service/impl/UserServiceImpl.java:
--------------------------------------------------------------------------------
1 | package com.zx.springmybatis.service.impl;
2 |
3 | import com.github.pagehelper.PageHelper;
4 | import com.github.pagehelper.PageInfo;
5 | import com.zx.springmybatis.dao.UserMapper;
6 | import com.zx.springmybatis.entity.User;
7 | import com.zx.springmybatis.service.UserService;
8 | import lombok.extern.slf4j.Slf4j;
9 | import org.springframework.beans.factory.annotation.Autowired;
10 | import org.springframework.stereotype.Service;
11 |
12 | import java.util.List;
13 |
14 | /**
15 | * author:Administrator
16 | * datetime:2017/11/7 0007 10:14
17 | */
18 | @Service
19 | @Slf4j
20 | public class UserServiceImpl implements UserService {
21 | @Autowired
22 | private UserMapper userMapper;
23 |
24 | @Override
25 | public List getAll() {
26 | return userMapper.selectAll();
27 | }
28 |
29 | //分页测试
30 | @Override
31 | public PageInfo getAllForPage(Integer pageNum, Integer pageSize) {
32 | pageNum = pageNum == null ? 1 : pageNum;
33 | pageSize = pageSize == null ? 10 : pageSize;
34 |
35 | PageHelper.startPage(pageNum,pageSize);
36 | List userList = userMapper.selectAll();
37 | PageInfo pageInfo = new PageInfo<>(userList);
38 |
39 | log.info("pageInfo:{}",pageInfo);
40 | return pageInfo;
41 | }
42 |
43 |
44 |
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/src/main/resources/generator/generatorConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
33 |
34 |
--------------------------------------------------------------------------------
/src/main/resources/sql/test1.sql:
--------------------------------------------------------------------------------
1 | /*
2 | Navicat MySQL Data Transfer
3 |
4 | Source Server : localhost
5 | Source Server Version : 50720
6 | Source Host : localhost:3306
7 | Source Database : test1
8 |
9 | Target Server Type : MYSQL
10 | Target Server Version : 50720
11 | File Encoding : 65001
12 |
13 | Date: 2017-12-01 18:01:14
14 | */
15 |
16 | SET FOREIGN_KEY_CHECKS=0;
17 |
18 | -- ----------------------------
19 | -- Table structure for grade
20 | -- ----------------------------
21 | DROP TABLE IF EXISTS `grade`;
22 | CREATE TABLE `grade` (
23 | `id` bigint(20) NOT NULL AUTO_INCREMENT,
24 | `name` varchar(255) DEFAULT NULL,
25 | PRIMARY KEY (`id`)
26 | ) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8mb4;
27 |
28 | -- ----------------------------
29 | -- Table structure for test
30 | -- ----------------------------
31 | DROP TABLE IF EXISTS `test`;
32 | CREATE TABLE `test` (
33 | `id` bigint(20) NOT NULL,
34 | `name` varchar(255) DEFAULT NULL,
35 | `update_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' ON UPDATE CURRENT_TIMESTAMP,
36 | PRIMARY KEY (`id`)
37 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
38 |
39 | -- ----------------------------
40 | -- Table structure for user
41 | -- ----------------------------
42 | DROP TABLE IF EXISTS `user`;
43 | CREATE TABLE `user` (
44 | `id` bigint(20) NOT NULL AUTO_INCREMENT,
45 | `name` varchar(255) DEFAULT NULL,
46 | `password` varchar(255) DEFAULT NULL,
47 | `login_address` varchar(255) DEFAULT NULL,
48 | `grade_id` bigint(20) DEFAULT NULL,
49 | PRIMARY KEY (`id`)
50 | ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4;
51 |
--------------------------------------------------------------------------------
/src/main/java/com/zx/springmybatis/dao/GradeMapper.java:
--------------------------------------------------------------------------------
1 | package com.zx.springmybatis.dao;
2 |
3 |
4 | import com.zx.springmybatis.config.mybatis.CommonMapper;
5 | import com.zx.springmybatis.entity.Grade;
6 | import org.apache.ibatis.annotations.Insert;
7 | import org.apache.ibatis.annotations.InsertProvider;
8 | import org.springframework.stereotype.Repository;
9 |
10 | import java.util.List;
11 | import java.util.Map;
12 |
13 |
14 | @Repository
15 | public interface GradeMapper extends CommonMapper {
16 |
17 |
18 | /**
19 | * 最原始批量增加
20 | */
21 | @Insert("")
25 | void addAll(List grades);
26 |
27 | /**
28 | * 使用Provider批量增加
29 | */
30 | @InsertProvider(type = Provider.class,method = "batchInsert")
31 | void addAll1(List list);
32 |
33 | /**
34 | * 使用内部类作为Provider
35 | */
36 | class Provider{
37 | /**
38 | * 返回String作为sql语句
39 | * 不使用SQL构建器
40 | * 此处的sql是原生sql
41 | *
42 | * 参数: map中存储了MyBatisMapper方法中的参数;
43 | * 如果方法只有一个参数,也可以直接写相同类型的参数直接接收;
44 | * 如果方法使用了@Param注解,则使用map用@Param的value作为key接收
45 | * 如果多个参数,且未使用@Param注解,则使用map,用索引作为key接收
46 | * 具体可以下断点自行查看map
47 | */
48 | public String batchInsert(Map map) {
49 | List list = (List) map.get("list");
50 | StringBuilder result = new StringBuilder();
51 | result.append("INSERT INTO grade(name) VALUES");
52 | list.stream().forEach(item ->{
53 | result.append("(").append("\"" + item.getName() + "\"").append(")");
54 | result.append(",");
55 | });
56 | result.deleteCharAt(result.length()-1);
57 | return result.toString();
58 | }
59 | }
60 |
61 | }
--------------------------------------------------------------------------------
/src/main/java/com/zx/springmybatis/config/cache/CustomRedisCacheExpireProperties.java:
--------------------------------------------------------------------------------
1 | package com.zx.springmybatis.config.cache;
2 |
3 | import lombok.Data;
4 | import org.springframework.boot.context.properties.ConfigurationProperties;
5 | import org.springframework.stereotype.Component;
6 | import org.springframework.util.CollectionUtils;
7 |
8 | import java.util.HashMap;
9 | import java.util.List;
10 | import java.util.Map;
11 |
12 | /**
13 | * author:ZhengXing
14 | * datetime:2017/12/1 0001 12:46
15 | * 自定义的redis缓存中的过期时间属性
16 | */
17 | @Data
18 | @ConfigurationProperties(prefix = "spring.cache")
19 | @Component
20 | public class CustomRedisCacheExpireProperties {
21 | //该属性在spring cache框架自己的类中也会被获取
22 | //此处获取是为了对长度进行校验,防止 缓存名字 - 缓存时间 没有一一匹配
23 | private List cacheNames;
24 |
25 | //缓存时间,和缓存名一一对应
26 | private List cacheExpires;
27 |
28 | /**
29 | * 生成Map,用来放入RedisManager中
30 | */
31 | public Map generateExpireMap() {
32 | Map expireMap = new HashMap<>();
33 | /**
34 | * 校验参数值
35 | */
36 | //如果未配置cacheNames属性,返回空map
37 | //如果未配置cacheExpires属性,也返回空map
38 | if (CollectionUtils.isEmpty(cacheNames) || CollectionUtils.isEmpty(cacheExpires))
39 | return expireMap;
40 | //长度校验:只要数组不为空,有x个cacheNames,就需要x个cacheExpires,如果某个name无需缓存时间,设置为0即可
41 | //其内部实现就是使用该Map生成若干个RedisCacheMetadata,该对象和cacheName一一对应,并且其中的默认过期时间就是0
42 | //不对.我在redis中试了下,将key过期时间设为0或负数,该key会直接过期.
43 | //找了很久..没找到其判断过期时间的代码
44 | if(cacheNames.size() != cacheExpires.size())
45 | //此处随便抛出一个非法状态异常,可自定义异常抛出
46 | throw new IllegalStateException("cacheExpires设置非法.cacheNames和cacheExpires长度不一致");
47 | //遍历cacheNames
48 | for (int i = 0; i < cacheNames.size(); i++) {
49 | //只有当cacheExpires设置的大于0时,才放入map,否则就使用默认的过期时间
50 | long expire = cacheExpires.get(i);
51 | if (expire > 0)
52 | expireMap.put(cacheNames.get(i),expire);
53 | }
54 | return expireMap;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/main/java/com/zx/springmybatis/dao/driver/CommonConditionLanguageDriver.java:
--------------------------------------------------------------------------------
1 | package com.zx.springmybatis.dao.driver;
2 |
3 | import com.google.common.base.CaseFormat;
4 | import org.apache.ibatis.mapping.SqlSource;
5 | import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver;
6 | import org.apache.ibatis.session.Configuration;
7 |
8 | import java.lang.reflect.Field;
9 |
10 | /**
11 | * author:ZhengXing
12 | * datetime:2017/11/28 0028 14:19
13 | * 通用条件查询语言驱动
14 | */
15 | public class CommonConditionLanguageDriver extends XMLLanguageDriver{
16 | /**
17 | * 重写父类方法,以便在条件查询时,将不为空属性,加入where条件,
18 | * 例如:
19 | * select * from user
20 | *
21 | * and username=#{username}
22 | * and password=#{password}
23 | *
24 | * parameterType:mapper中方法接收的参数,如果参数有多个,其值为map,当参数为多个时,无法获悉每个参数的类型(应该是)
25 | */
26 | @Override
27 | public SqlSource createSqlSource(Configuration configuration, String script, Class> parameterType) {
28 | //追加where
29 | StringBuilder sql = new StringBuilder().append("");
30 | //默认将该参数类型作为实体类类型处理,获取所有属性
31 | Field[] fields = parameterType.getDeclaredFields();
32 |
33 | //遍历实体类的每个属性
34 | for (Field field : fields) {
35 | //将java中 userId形式的属性转换为数据库中 user_id形式的
36 | String sqlField = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, field.getName());
37 | //循环增加的语句
38 | String temp = "and sqlField=#{javaField} ";
39 | //将字符串中的自定义标识字符:javaField和sqlField替换
40 | temp = temp.replaceAll("javaField",field.getName())
41 | .replaceAll("sqlField", sqlField);
42 | sql.append(temp);
43 | }
44 | sql.append("");
45 |
46 | //增加";
48 | //继续执行父类的方法实现,构建SqlSource
49 | return super.createSqlSource(configuration, script, parameterType);
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/test/java/com/zx/springmybatis/service/impl/CacheServiceTest.java:
--------------------------------------------------------------------------------
1 | package com.zx.springmybatis.service.impl;
2 |
3 | import com.sun.xml.internal.ws.api.model.CheckedException;
4 | import com.zx.springmybatis.dto.GradeDTO;
5 | import com.zx.springmybatis.entity.Grade;
6 | import com.zx.springmybatis.entity.User;
7 | import lombok.NonNull;
8 | import lombok.SneakyThrows;
9 | import lombok.extern.slf4j.Slf4j;
10 | import org.junit.Test;
11 | import org.junit.runner.RunWith;
12 | import org.springframework.beans.factory.annotation.Autowired;
13 | import org.springframework.boot.test.context.SpringBootTest;
14 | import org.springframework.test.context.junit4.SpringRunner;
15 |
16 | import java.io.UnsupportedEncodingException;
17 | import java.util.List;
18 | import java.util.concurrent.TimeUnit;
19 |
20 | /**
21 | * author:ZhengXing
22 | * datetime:2017/11/29 0029 10:51
23 | * 缓存测试
24 | */
25 | @SpringBootTest
26 | @RunWith(SpringRunner.class)
27 | @Slf4j
28 | public class CacheServiceTest {
29 | @Autowired
30 | private CacheService cacheService;
31 |
32 | @Test
33 | public void test1() {
34 | log.info("第一次查询");
35 | findAll();
36 | log.info("第二次查询");
37 | findAll();
38 | }
39 |
40 | @Test
41 | @SneakyThrows(InterruptedException.class)//lombok 消除受检异常
42 | public void test2() {
43 | log.info("第一次查询");
44 | findGradeForUsers();
45 | TimeUnit.SECONDS.sleep(2);
46 | log.info("第二次查询");
47 | findGradeForUsers();
48 | }
49 |
50 |
51 | @Test
52 | public void test3() {
53 | findAll();
54 | findAll();
55 |
56 | Grade grade = Grade.builder()
57 | .name("aaaaa").build();
58 | cacheService.updateGradeById(1L, grade);
59 |
60 | // findAll();
61 | }
62 |
63 | @Test
64 | public void test() {
65 | Grade grade1 = cacheService.insertGrade(Grade.builder()
66 | .name("xxxxxxxx").build());
67 | System.out.println("新增成功:" + grade1);
68 | Grade grade = cacheService.findOneByGradeId(grade1.getId());
69 | System.out.println(grade);
70 | }
71 |
72 | private void findAll() {
73 | List grades = cacheService.findAll();
74 | grades.forEach(item -> {
75 | System.out.println(grades);
76 | });
77 | }
78 |
79 | private void findGradeForUsers() {
80 | GradeDTO gradeDTO = cacheService.findGradeForUsers(1L);
81 | System.out.println(gradeDTO);
82 | }
83 |
84 |
85 | }
--------------------------------------------------------------------------------
/src/main/java/com/zx/springmybatis/service/impl/CacheService.java:
--------------------------------------------------------------------------------
1 | package com.zx.springmybatis.service.impl;
2 |
3 | import com.zx.springmybatis.dao.GradeMapper;
4 | import com.zx.springmybatis.dao.UserMapper;
5 | import com.zx.springmybatis.dto.GradeDTO;
6 | import com.zx.springmybatis.entity.Grade;
7 | import com.zx.springmybatis.entity.User;
8 | import lombok.extern.slf4j.Slf4j;
9 | import org.springframework.beans.factory.annotation.Autowired;
10 | import org.springframework.cache.annotation.*;
11 | import org.springframework.stereotype.Service;
12 | import tk.mybatis.mapper.entity.Example;
13 |
14 | import java.util.List;
15 |
16 | /**
17 | * author:ZhengXing
18 | * datetime:2017/11/29 0029 10:27
19 | *
20 | * 缓存测试类
21 | */
22 | @Service
23 | @Slf4j
24 | @CacheConfig(cacheNames = "a")
25 | public class CacheService {
26 | @Autowired
27 | private UserMapper userMapper;
28 |
29 | @Autowired
30 | private GradeMapper gradeMapper;
31 |
32 | /**
33 | * 查询所有班级
34 | * 注意,@Cacheable中的cacheNames值需要在yml中配置,也就是spring.cache.cache-names
35 | * 如果需要指定一个特别的固定的key,需要使用单引号包裹
36 | */
37 | @Cacheable
38 | @Caching
39 | public List findAll() {
40 | log.info("查询所有班级");
41 | return gradeMapper.selectAll();
42 | }
43 |
44 | /**
45 | * 修改某个班级
46 | * 修改班级后,清空findAll方法的缓存
47 | */
48 | @CacheEvict(key = "'CacheService-findAll-'")
49 | public void updateGradeById(Long gradeId,Grade grade) {
50 |
51 | Example example = new Example(Grade.class);
52 | example.createCriteria().andEqualTo("id", gradeId);
53 |
54 | int i = gradeMapper.updateByExampleSelective(grade, example);
55 | //根据id直接更新
56 | //gradeMapper.updateByExampleSelective();
57 | System.out.println("更新条数:" + i);
58 | }
59 |
60 | /**
61 | * 查询单个班级,根据id
62 | */
63 | @Cacheable
64 | public Grade findOneByGradeId(Long gradeId) {
65 | return gradeMapper.selectByPrimaryKey(gradeId);
66 | }
67 |
68 | /**
69 | * 增加班级方法.
70 | * 插入时将其直接存入缓存;
71 | * 注意,这样就需要返回该对象整个
72 | * 关于id,此处已经自动回写了
73 | */
74 | @CachePut(key = "'CacheService-findOneByGradeId-' + #result.id")
75 | public Grade insertGrade(Grade grade) {
76 | gradeMapper.insert(grade);
77 | return grade;
78 | }
79 |
80 |
81 | /**
82 | * 查询某个班级和班级下所有学生
83 | */
84 | @Cacheable(cacheNames = "c")
85 | public GradeDTO findGradeForUsers(Long gradeId) {
86 | log.info("查询班级和其下所有学生");
87 | Grade grade = gradeMapper.selectByPrimaryKey(gradeId);
88 | List users = userMapper.select(new User().setGradeId(gradeId));
89 | return new GradeDTO(grade, users);
90 | }
91 |
92 |
93 | }
94 |
--------------------------------------------------------------------------------
/src/test/java/com/zx/springmybatis/dao/UserMapperTest.java:
--------------------------------------------------------------------------------
1 | package com.zx.springmybatis.dao;
2 |
3 | import com.zx.springmybatis.entity.Grade;
4 | import com.zx.springmybatis.entity.User;
5 | import lombok.extern.slf4j.Slf4j;
6 | import org.apache.ibatis.reflection.ArrayUtil;
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 | import org.springframework.beans.factory.annotation.Autowired;
10 | import org.springframework.boot.test.context.SpringBootTest;
11 | import org.springframework.data.redis.core.RedisTemplate;
12 | import org.springframework.data.redis.core.ValueOperations;
13 | import org.springframework.test.context.junit4.SpringRunner;
14 | import org.springframework.util.CollectionUtils;
15 | import tk.mybatis.mapper.entity.Example;
16 |
17 | import java.util.Arrays;
18 | import java.util.Collection;
19 | import java.util.List;
20 | import java.util.stream.Collectors;
21 |
22 | import static org.junit.Assert.*;
23 |
24 | /**
25 | * author:ZhengXing
26 | * datetime:2017/11/16 0016 15:58
27 | */
28 | @SpringBootTest
29 | @RunWith(SpringRunner.class)
30 | @Slf4j
31 | public class UserMapperTest {
32 |
33 | @Autowired
34 | private UserMapper userMapper;
35 |
36 | @Autowired
37 | private GradeMapper gradeMapper;
38 |
39 | @Test
40 | public void test() {
41 | User temp = User.builder().id(1L).build();
42 |
43 | Example example = new Example(User.class)//传入实体类对象构造
44 | .selectProperties("id", "name")//设置要查询的字段
45 | .excludeProperties("id");//设置不查询的字段,与要查询同时设置,要查询的优先
46 | example.orderBy("id").desc();//排序
47 | example.createCriteria();//其他方法类似,基本都能用方法名理解
48 | // .andLessThan("id","4");//查询属性小于该值的记录
49 | // .andGreaterThan("id","4");//查询属性大于该值的记录
50 | // .andAllEqualTo(temp);//查询字段值等于该对象的属性值的记录,所有属性。
51 | // .andEqualTo(temp);//查询字段值等于该对象的属性值的记录,非空属性。
52 | // .andBetween("name","a","c");//between查询
53 | // .andCondition("name = 'a' or name ='b'");//可以直接使用sql查询,此处输入where后面的字符
54 |
55 | List userList = userMapper.selectByExample(example);
56 | userList.forEach(user -> {
57 | log.info("user:{}", user);
58 | });
59 | }
60 |
61 | @Test
62 | public void test1() {
63 | List grades = Arrays.asList(new Grade("a"), new Grade("b"), new Grade("c"));
64 | gradeMapper.addAll(grades);
65 | }
66 |
67 | @Test
68 | public void test2() {
69 |
70 | User user = new User().setName("a").setPassword("aa");
71 | List a = userMapper.findByCondition(user);
72 | a.forEach(item -> System.out.println(a));
73 | }
74 |
75 | @Test
76 | public void test3() {
77 | User a = userMapper.a(1L);
78 | System.out.println(a);
79 | }
80 |
81 | /**
82 | * 测试redis
83 | */
84 | @Autowired
85 | private RedisTemplate redisTemplate;
86 |
87 | @Test
88 | public void test4(){
89 | redisTemplate.opsForValue().set("a","test");
90 | }
91 |
92 |
93 | }
--------------------------------------------------------------------------------
/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
10 |
11 |
13 |
14 |
15 |
16 | ${CONSOLE_LOG_PATTERN}
17 | utf8
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | ERROR
26 |
27 | DENY
28 |
29 | ACCEPT
30 |
31 |
32 |
33 | [%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-36.36thread] [%-5level] [%-36.36logger{36}:%-4.4line] - %msg%n
34 |
35 |
36 |
37 |
38 |
39 | /log/smsSender/info.%d.log
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | ERROR
48 |
49 |
50 |
51 | [%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-36.36thread] [%-5level] [%-36.36logger{36}:%-4.4line] - %msg%n
52 |
53 |
54 |
55 |
56 |
57 | /log/smsSender/error.%d.log
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | server:
2 | port: 80
3 | spring:
4 | datasource:
5 | # 可自动识别
6 | driver-class-name: com.mysql.jdbc.Driver
7 | username: root
8 | password: 970389
9 | type: com.alibaba.druid.pool.DruidDataSource
10 | url: jdbc:mysql://127.0.0.1:3306/test1?useSSL=false
11 | # DataSource配置
12 | druid:
13 | # 初始容量
14 | initial-size: 10
15 | # 最大连接池个数
16 | max-active: 20
17 | # 最小空闲
18 | min-idle: 10
19 | # 获取连接最大等待时间
20 | max-wait: 3000
21 | # 是否缓存preparedStatement(PSCache),对游标提升巨大,建议oracle开启,mysql关闭
22 | pool-prepared-statements: false
23 | # 启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100
24 | max-pool-prepared-statement-per-connection-size: 0
25 | # 检测连接是否有效的sql,要求是一个查询语句,常用select 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。
26 | validation-query: select 'x'
27 | # 检测连接是否有效的超时时间。秒,底层调用jdbc Statement对象的void setQueryTimeout(int seconds)方法
28 | validation-query-timeout: 30
29 | # 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
30 | test-on-borrow: false
31 | # 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
32 | test-on-return: false
33 | # 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
34 | test-while-idle: true
35 | # 驱逐策略间隔,如果连接空闲时间大于minEvictableIdleTimeMillis,则关闭
36 | time-between-eviction-runs-millis: 60000
37 | # 在池中的最小生存时间
38 | min-evictable-idle-time-millis: 30000
39 | # 在池中的最大生存时间
40 | max-evictable-idle-time-millis: 600000
41 | # 连接池中的minIdle数量以内的连接,空闲时间超过minEvictableIdleTimeMillis,则会执行keepAlive操作。
42 | keep-alive: true
43 | # 连接初始化时,执行的sql
44 | connection-init-sqls:
45 | # 开启的过滤器,常用的有 监控统计:stat 日志:log4j 防御sql注入:wall
46 | filters: stat,wall,log4j
47 | # 合并多个dataSource的监控记录
48 | use-global-data-source-stat: true
49 |
50 | # 监控配置
51 | # 是否启用stat-filter默认值true
52 | web-stat-filter.enabled: true
53 | # 匹配的uri
54 | web-stat-filter.url-pattern: /*
55 | # 忽略的uri *.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*
56 | web-stat-filter.exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*"
57 | # 是否启用session统计
58 | web-stat-filter.session-stat-enable: true
59 | web-stat-filter.session-stat-max-count: 1000
60 | web-stat-filter.principal-session-name: zx
61 | web-stat-filter.principal-cookie-name: zx
62 | # 监控单个url调用的sql列表。
63 | web-stat-filter.profile-enable: true
64 | # StatViewServlet配置,说明请参考Druid Wiki,配置_StatViewServlet配置
65 | #是否启用监控界面默认值true
66 | stat-view-servlet.enabled: true
67 | # web.xml的url-pattern,也就是访问/druid/*访问到该servlet
68 | stat-view-servlet.url-pattern: /druid/*
69 | # 允许清空统计数据
70 | stat-view-servlet.reset-enable: true
71 | # 用户名
72 | stat-view-servlet.login-username: zx
73 | # 密码
74 | stat-view-servlet.login-password: 123456
75 | # ip白名单
76 | stat-view-servlet.allow:
77 | # ip黑名单
78 | stat-view-servlet.deny:
79 |
80 | # 过滤器配置
81 | filter:
82 | stat:
83 | # 聚合sql 开启慢sql查询
84 | merge-sql: true
85 | # 是否开启慢sql查询
86 | log-slow-sql: true
87 | # 超过多少时间为慢sql 开启慢sql查询
88 | slow-sql-millis: 1
89 | # 是否启用
90 | enabled: true
91 | # 安全配置,防止sql注入. 具体参数可查看文档,包括禁止各类增删查改的操作
92 | # wall:
93 | # config:
94 |
95 | #缓存
96 | cache:
97 | #缓存名字
98 | cache-names: #该属性的接收类型为list,得在这样写才可以分为一个个元素
99 | - a
100 | - b
101 | - c
102 | #缓存过期时间
103 | cacheExpires: #自定义属性,也是list,用来配置缓存过期时间,值为0时根据我的逻辑是使用默认超时时间的
104 | - 3600
105 | - 1
106 | - 0
107 | #缓存类型,同时引入guava包和redis时,不配置可能有bug
108 | type: redis
109 | #redis配置
110 | redis:
111 | host: 106.14.7.29
112 | port: 6379
113 | password: 970389
114 | pool:
115 | max-active: 10
116 | max-idle: 1
117 | min-idle: 0
118 | max-wait: 5000
119 | timeout: 5000
120 | database: 0
121 | output:
122 | ansi:
123 | enabled: always
124 | mybatis:
125 | configuration:
126 | # 驼峰
127 | map-underscore-to-camel-case: true
128 | mapper-locations: classpath:mapper/*.xml
129 | type-aliases-package: com.zx.springmybatis.entity
130 |
131 | # 输出MyBatis语句,trace会输出结果,debug只输出语句
132 | logging:
133 | level:
134 | com:
135 | zx:
136 | springmybatis:
137 | dao: debug
138 |
139 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | com.zx
7 | springmybatis
8 | 0.0.1-SNAPSHOT
9 | jar
10 |
11 | SpringBoot-MyBatis-RedisCache
12 | spring整合mybatis和通用mapper
13 |
14 |
15 |
16 | org.springframework.boot
17 | spring-boot-starter-parent
18 | 1.5.8.RELEASE
19 |
20 |
21 |
22 |
23 | UTF-8
24 | UTF-8
25 | 1.8
26 | 1.1.6
27 | 1.3.1
28 | 2.1.5
29 | 1.1.5
30 | 1.2.3
31 | 1.3.5
32 |
33 |
34 |
35 |
36 | ${basedir}/src/main/java
37 | tk.mybatis.mapper.mapper
38 | tk.mybatis.mapper.model
39 |
40 |
41 |
42 |
43 | 3.4.4
44 | 5.1.44
45 | 23.5-jre
46 |
47 |
48 |
49 |
50 | org.mybatis.spring.boot
51 | mybatis-spring-boot-starter
52 | 1.3.1
53 |
54 |
55 | org.springframework.boot
56 | spring-boot-starter-web
57 |
58 |
59 |
60 | mysql
61 | mysql-connector-java
62 | runtime
63 |
64 |
65 | org.springframework.boot
66 | spring-boot-starter-test
67 | test
68 |
69 |
70 |
71 | org.projectlombok
72 | lombok
73 | provided
74 |
75 |
76 |
77 | org.mybatis.spring.boot
78 | mybatis-spring-boot-starter
79 | ${mybatis-spring-boot-starter.version}
80 |
81 |
82 |
83 |
84 |
85 | com.alibaba
86 | druid-spring-boot-starter
87 | ${druid.version}
88 |
89 |
90 |
91 | tk.mybatis
92 | mapper-spring-boot-starter
93 | ${mapper-spring-boot-starter.version}
94 |
95 |
96 |
97 | com.github.pagehelper
98 | pagehelper-spring-boot-starter
99 | ${pagehelper-spring-boot-starter.version}
100 |
101 |
102 |
103 | com.google.guava
104 | guava
105 | ${guava.version}
106 |
107 |
108 |
109 | org.springframework.boot
110 | spring-boot-starter-cache
111 |
112 |
113 |
114 | org.springframework.boot
115 | spring-boot-starter-data-redis
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 | org.springframework.boot
125 | spring-boot-maven-plugin
126 |
127 |
128 |
129 | org.mybatis.generator
130 | mybatis-generator-maven-plugin
131 | 1.3.5
132 |
133 | ${basedir}/src/main/resources/generator/generatorConfig.xml
134 | true
135 | true
136 |
137 |
138 |
139 | mysql
140 | mysql-connector-java
141 | ${mysql.version}
142 |
143 |
144 | tk.mybatis
145 | mapper
146 | ${mapper.version}
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
--------------------------------------------------------------------------------
/src/main/java/com/zx/springmybatis/config/cache/RedisCacheConfig.java:
--------------------------------------------------------------------------------
1 | package com.zx.springmybatis.config.cache;
2 |
3 | import com.fasterxml.jackson.annotation.JsonAutoDetect;
4 | import com.fasterxml.jackson.annotation.PropertyAccessor;
5 | import com.fasterxml.jackson.databind.ObjectMapper;
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.cache.Cache;
8 | import org.springframework.cache.CacheManager;
9 | import org.springframework.cache.annotation.CachingConfigurerSupport;
10 | import org.springframework.cache.interceptor.CacheErrorHandler;
11 | import org.springframework.cache.interceptor.CacheResolver;
12 | import org.springframework.cache.interceptor.KeyGenerator;
13 | import org.springframework.cache.interceptor.SimpleCacheErrorHandler;
14 | import org.springframework.context.annotation.Bean;
15 | import org.springframework.context.annotation.Configuration;
16 | import org.springframework.data.redis.cache.RedisCacheManager;
17 | import org.springframework.data.redis.connection.RedisConnectionFactory;
18 | import org.springframework.data.redis.core.RedisTemplate;
19 | import org.springframework.data.redis.core.StringRedisTemplate;
20 | import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
21 |
22 | import java.util.*;
23 |
24 |
25 | /**
26 | * author:ZhengXing
27 | * datetime:2017/11/29 0029 13:32
28 | * redis缓存配置类
29 | *
30 | * CachingConfigurerSupport该类使用空方法实现了CachingConfigurer接口,
31 | * 子类只需要实现想要自定义的方法即可配置 缓存管理器/主键生成器/缓存解析器/异常处理器等;
32 | * 如果不实现该接口,配置该类后,还需在注解中指定对应的keyGenerator才能生效
33 | *
34 | */
35 | @Configuration
36 | public class RedisCacheConfig extends CachingConfigurerSupport{
37 |
38 | //Spring构造的redis连接工厂
39 | @Autowired
40 | private RedisConnectionFactory redisConnectionFactory;
41 |
42 | //自定义的用来读取yml文件中每个缓存名对应的缓存过期时间的属性类
43 | @Autowired
44 | private CustomRedisCacheExpireProperties customRedisCacheExpireProperties;
45 |
46 | /**
47 | * 匿名内部类构建主键生成器
48 | * 其参数分别为 调用缓存的类(service)/调用缓存的方法/方法的参数列表
49 | */
50 | @Bean
51 | @Override
52 | public KeyGenerator keyGenerator() {
53 | return (object,method,params)->{
54 | //类名:方法名:参数[0]参数[1]...
55 | StringBuilder key = new StringBuilder(object.getClass().getSimpleName() + "-" + method.getName() + "-");
56 | for (Object param : params) {
57 | //直接追加,只要该参数是基本类型或实现了toString方法,就没问题,否则会显示xx@hashcode那种类型的字符
58 | //如果参数过多,需要自定义key
59 | key.append(param.toString());
60 | }
61 | return key.toString();
62 | };
63 | }
64 |
65 | /**
66 | * 配置RedisTemplate
67 | * 是为了替换默认的JDK的序列化器,使用默认的序列化器,key会乱码;
68 | *
69 | * 此处在Spring中的实现是,他有一个默认的RedisTemplate Bean,但使用了
70 | * @ConditionalOnMissingBean(type = RedisTemplate.class)这样一个注解,
71 | * 表示在我们没有配置自定义的bean的情况下,才使用它默认的bean
72 | */
73 | @Bean
74 | public RedisTemplate redisTemplate() {
75 | //创建StringRedis模版
76 | StringRedisTemplate stringRedisTemplate = new StringRedisTemplate(redisConnectionFactory);
77 | // 使用Jackson2JsonRedisSerialize 替换默认序列化
78 | Jackson2JsonRedisSerializer> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
79 | ObjectMapper objectMapper = new ObjectMapper();
80 | objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
81 | objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
82 | jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
83 |
84 | //value使用jackJson序列化,key使用string序列化,string序列化不支持list等类型
85 | // stringRedisTemplate.setKeySerializer(new StringRedisSerializer());//不需要该设置,key也不会乱码.
86 | stringRedisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
87 |
88 | //InitializingBean接口提供的一个方法,在spring容器属性被初始化完成后再调用该方法
89 | stringRedisTemplate.afterPropertiesSet();
90 |
91 | return stringRedisTemplate;
92 | }
93 |
94 | /**
95 | * 创建缓存管理器
96 | * 主要为了自定义若干cacheNames和缓存过期时间;
97 | *
98 | * 自定义该类后,如果缓存注解中使用了一个未配置的缓存名,并且,该类的一个dynamic属性为true,
99 | * 就会生成一个新的以该名字为名的{@link Cache}对象,放入集合;
100 | * 但如果给该缓存管理器配置了cacheNames(也就是调用了setCacheNames()方法),该dynamic属性就会被
101 | * 设置为false,将无法动态加入缓存名;那么就会抛出无法找到该缓存的异常;
102 | * 我觉得还是设置上比较好.
103 | */
104 | @Bean
105 | @Override
106 | public CacheManager cacheManager() {
107 | RedisCacheManager redisCacheManager = new RedisCacheManager(redisTemplate());
108 | //默认的过期时间,会被每个缓存名自己的过期时间覆盖
109 | redisCacheManager.setDefaultExpiration(3600);
110 | /**
111 | * 启动时加载远程缓存; 不开启:每次第一次查询即使缓存中已经有旧的缓存,也不会读取到;
112 | * 开启后如果缓存中已有缓存,第一次查询就会从缓存中读取
113 | */
114 | redisCacheManager.setLoadRemoteCachesOnStartup(true);
115 | //开启后,key会携带上cacheName作为前缀
116 | redisCacheManager.setUsePrefix(true);
117 | /**
118 | * 设置cacheNames,也可以在构造函数中设置,此处我使用在yml配置的cacheNames即可
119 | * 需要注意的是,显而易见,此处的RedisCacheManager还未注入yml中的cacheNames;
120 | * 所以如果使用redisCacheManager.getCacheNames()取出的将是空的;
121 | * 但是,如果使用setExpires()方法,设置好对应的cacheName和过期时间,还是能够生效的
122 | */
123 | // redisCacheManager.setCacheNames(Arrays.asList(cacheNames));
124 | // Collection cacheNames = redisCacheManager.getCacheNames();
125 |
126 | //使用自定义的属性类,根据yml配置,生成缓存名和过期时间对应的map
127 | Map expires = customRedisCacheExpireProperties.generateExpireMap();
128 | //设置每个缓存对应的过期时间
129 | redisCacheManager.setExpires(expires);
130 | //给缓存管理器设置上缓存名s
131 | redisCacheManager.setCacheNames(customRedisCacheExpireProperties.getCacheNames());
132 |
133 |
134 | return redisCacheManager;
135 | }
136 |
137 | /**
138 | * 自定义缓存异常处理器.
139 | * 该CacheErrorHandler接口只有一个实现类SimpleCacheErrorHandler.只是抛出了所有异常未做任何处理
140 | * 有若干个方法,分别处理获取/修改/放入/删除缓存异常.
141 | * 若有需要.可自定义实现,比如因为缓存不是必须的,那就可以只做日志记录,不再抛出异常
142 | *
143 | */
144 | @Bean
145 | @Override
146 | public CacheErrorHandler errorHandler() {
147 | return new SimpleCacheErrorHandler(){
148 | @Override
149 | public void handleCacheGetError(RuntimeException exception, Cache cache, Object key) {
150 | super.handleCacheGetError(exception, cache, key);
151 | }
152 | };
153 | }
154 |
155 | /**
156 | * 自定义缓存解析器(该类必须是线程安全的)
157 | * 解析该缓存由哪些(可以多个) cacheNames处理
158 | *
159 | * 其默认实现是SimpleCacheResolver
160 | *
161 | */
162 | @Override
163 | public CacheResolver cacheResolver() {
164 | return super.cacheResolver();
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | #### SpringBoot整合MyBatis/通用mapper/PageHelper,学习MyBatis注解/注解形式的动态sql等
2 | https://gitee.com/free/Mapper mapper主页
3 | https://gitee.com/free/Mapper/blob/master/wiki/mapper3/5.Mappers.md mapper所有方法
4 | https://gitee.com/free/Mybatis_Utils/blob/master/MybatisGeneator/MybatisGeneator.md MybatisGeneator插件学习
5 | http://blog.csdn.net/gebitan505/article/details/54929287
6 |
7 |
8 | #### 记录
9 | * 在github逛到一个支付宝支付的无需申请支付宝api的项目...文档大略看了一遍就把项目撸下来了.
10 | 想看看它是如何实现..知道对方已经支付成功的...然后就看见...他妈的..对方创建订单后..通知管理员,
11 | 然后管理员打开自己的支付宝,通过比对金额和邮箱等信息,确认对方支付,手动修改状态....我的天..
12 | 算了...也算是一个可行的个人支付方案把..
13 |
14 | #### bug
15 | * 如果出现无法读取yml文件的错误,检查yml文件的编码,删除所有中文即可
16 |
17 | * 在aop方法等处,获取到request对象
18 | > HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
19 |
20 |
21 | #### 奇淫巧技
22 | * 在github上随便看的xpay项目中的,比较不错的获取ip的方法.
23 | >
24 | /**
25 | * 获取客户端IP地址
26 | * @param request 请求
27 | * @return
28 | */
29 | public static String getIpAddr(HttpServletRequest request) {
30 | String ip = request.getHeader("x-forwarded-for");
31 | if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
32 | ip = request.getHeader("Proxy-Client-IP");
33 | }
34 | if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
35 | ip = request.getHeader("WL-Proxy-Client-IP");
36 | }
37 | if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
38 | ip = request.getRemoteAddr();
39 | if (ip.equals("127.0.0.1")) {
40 | //根据网卡取本机配置的IP
41 | InetAddress inet = null;
42 | try {
43 | inet = InetAddress.getLocalHost();
44 | } catch (UnknownHostException e) {
45 | e.printStackTrace();
46 | }
47 | ip = inet.getHostAddress();
48 | }
49 | }
50 | // 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
51 | if (ip != null && ip.length() > 15) {
52 | if (ip.indexOf(",") > 0) {
53 | ip = ip.substring(0, ip.indexOf(","));
54 | }
55 | }
56 | return ip;
57 | }
58 | >
59 |
60 | * 使用logback后,让控制台恢复彩色日志(该操作在Spring Boot官方文档中有更详细的说明)
61 | >
62 | logback.xml如下配置
63 |
64 |
65 |
66 |
67 |
68 |
70 |
71 |
73 |
74 |
75 |
76 | ${CONSOLE_LOG_PATTERN}
77 | utf8
78 |
79 |
80 |
81 | yml增加如下配置
82 | spring:
83 | output:
84 | ansi:
85 | enabled: always
86 | >
87 |
88 | * SpringBoot默认日志格式:
89 | > [%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-36.36thread] [%-5level] [%-36.36logger{36}:%-4.4line] - %msg%n
90 |
91 | * IDEA, ctrl + backspace,快速删除
92 |
93 | * 使用System.out.printf("cacheName:%s",item); 格式化输出.注意时后缀时tf
94 |
95 | * IDEA/Spring Boot/yml文件中的属性中,按 CTRL + B ,可进入该属性注入的代码处..屌..无意中按了下
96 |
97 | * 想到了一个lombok中@NonNull注解比较好的使用方式,只要在异常处理类中处理NullPointException,将其封装成自定义异常处理即可;
98 | 这样,使用@NonNull注解后,就可以较为优雅地处理这类算是已经自己处理的异常了
99 |
100 | * Guava CaseFormat:驼峰命名转换工具类
101 |
102 | * !!!国人编写的一些框架千万不要傻逼的看jar中反编译的java代码,IDEA会提示你下载有javadoc的源码,下过来,
103 | 中文注解注解起飞,舒服(例如这个通用Mapper)
104 |
105 | * 使用MessageFormat可以将字符串中的若干标识符替换为指定文本.
106 | 例如"My name is {}",可以将指定文本填充到{};
107 | 或"My name {0} {1}",可以将一个String[]数组中的元素一次填充到{0},{1}
108 |
109 |
110 | ##### 配置Mybatis
111 | 1. 引入依赖:(此处需要添加version的原因是,该jar是mybatis提供的,spring无法自动提供版本号)
112 | >
113 |
114 | org.mybatis.spring.boot
115 | mybatis-spring-boot-starter
116 | ${mybatis-spring-boot-starter.version}
117 |
118 |
119 | mysql
120 | mysql-connector-java
121 | runtime
122 |
123 | >
124 |
125 |
126 | 2. 在Application类上增加注解@MapperScan("com.zx.springmybatis.dao"),扫描dao层
127 |
128 | 3. 然后就可以直接在dao类上使用mybatis注解了
129 |
130 | 4. 如下配置开启驼峰:
131 | >
132 | mybatis:
133 | configuration:
134 | #开启驼峰
135 | map-underscore-to-camel-case: true
136 | >
137 |
138 |
139 | 5. 如果需要使用mapper.xml,只需要在yml添加如下即可:
140 | >
141 | mapper-locations: classpath:mapper/*.xml #xml文件内容
142 | type-aliases-package: com.zx.springmybatis.entity #实体类包
143 | >
144 |
145 | #### 配置Druid - 未完全整合spring boot
146 | 4. 引入Druid依赖:
147 | >
148 |
149 | com.alibaba
150 | druid
151 | ${druid.version}
152 |
153 | >
154 |
155 | 5. 在yml文件中配置以spring.datasource开头的配置(具体配置参数可看DruidDataSource类源码)
156 |
157 | 6. 新建配置类,将DruidDataSource加入bean,并将yml中配置的参数注入
158 |
159 | @Configuration
160 | public class DruidDataSourceConfiguration {
161 | @Bean
162 | @ConfigurationProperties(prefix = "spring.datasource")
163 | public DataSource druidDataSource() {
164 | DruidDataSource druidDataSource = new DruidDataSource();
165 | return druidDataSource;
166 | }
167 | }
168 |
169 | 7. 配置Servlet(原web.xml):
170 |
171 | @WebServlet(urlPatterns = "/druid/*",
172 | initParams = {
173 | @WebInitParam(name = "allow",value = ""),// IP白名单 (没有配置或者为空,则允许所有访问)
174 | @WebInitParam(name = "deny",value = ""),// IP黑名单 (存在共同时,deny优先于allow)
175 | @WebInitParam(name = "loginUsername",value = "zx"),//用户名
176 | @WebInitParam(name = "loginPassword",value = "970389"),//密码
177 | @WebInitParam(name = "resetEnable",value = "false")// 禁用HTML页面上的“Reset All”功能
178 | })
179 | public class DruidStatViewServlet extends StatViewServlet{
180 | }
181 |
182 | 8. 配置Filter:
183 |
184 | @WebFilter(filterName="druidWebStatFilter",urlPatterns="/*",
185 | initParams={
186 | @WebInitParam(name="exclusions",value="*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*")// 忽略资源
187 | })
188 | public class DruidStatFilter extends WebStatFilter{
189 | }
190 |
191 | 9. 在Application类上加上@ServletComponentScan注解,让Servlet配置生效
192 |
193 |
194 | #### Spring Boot 整合 Druid
195 | * Druid的功能佷强大,包括sql记录/session监控/请求uri记录等
196 | * 依赖
197 | >
198 |
199 | com.alibaba
200 | druid-spring-boot-starter
201 | 1.1.6
202 |
203 | >
204 | * 配置属性
205 | >
206 | spring:
207 | datasource:
208 | # 可自动识别
209 | driver-class-name: com.mysql.jdbc.Driver
210 | username: root
211 | password: 970389
212 | type: com.alibaba.druid.pool.DruidDataSource
213 | url: jdbc:mysql://127.0.0.1:3306/test1?useSSL=false
214 | # DataSource配置
215 | druid:
216 | # 初始容量
217 | initial-size: 10
218 | # 最大连接池个数
219 | max-active: 20
220 | # 最小空闲
221 | min-idle: 10
222 | # 获取连接最大等待时间
223 | max-wait: 3000
224 | # 是否缓存preparedStatement(PSCache),对游标提升巨大,建议oracle开启,mysql关闭
225 | pool-prepared-statements: false
226 | # 启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100
227 | max-pool-prepared-statement-per-connection-size: 0
228 | # 检测连接是否有效的sql,要求是一个查询语句,常用select 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。
229 | validation-query: select 'x'
230 | # 检测连接是否有效的超时时间。秒,底层调用jdbc Statement对象的void setQueryTimeout(int seconds)方法
231 | validation-query-timeout: 30
232 | # 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
233 | test-on-borrow: false
234 | # 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
235 | test-on-return: false
236 | # 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
237 | test-while-idle: true
238 | # 驱逐策略间隔,如果连接空闲时间大于minEvictableIdleTimeMillis,则关闭
239 | time-between-eviction-runs-millis: 60000
240 | # 在池中的最小生存时间
241 | min-evictable-idle-time-millis: 30000
242 | # 在池中的最大生存时间
243 | max-evictable-idle-time-millis: 600000
244 | # 连接池中的minIdle数量以内的连接,空闲时间超过minEvictableIdleTimeMillis,则会执行keepAlive操作。
245 | keep-alive: true
246 | # 连接初始化时,执行的sql
247 | connection-init-sqls:
248 | # 开启的过滤器,常用的有 监控统计:stat 日志:log4j 防御sql注入:wall
249 | filters: stat,wall,log4j
250 | # 合并多个dataSource的监控记录
251 | use-global-data-source-stat: true
252 |
253 | # 监控配置
254 | # 是否启用stat-filter默认值true
255 | web-stat-filter.enabled: true
256 | # 匹配的uri
257 | web-stat-filter.url-pattern: /*
258 | # 忽略的uri
259 | web-stat-filter.exclusions: *.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*
260 | # 是否启用session统计
261 | web-stat-filter.session-stat-enable: false
262 | # web-stat-filter.session-stat-max-count:
263 | # web-stat-filter.principal-session-name:
264 | # web-stat-filter.principal-cookie-name:
265 | # 监控单个url调用的sql列表。
266 | web-stat-filter.profile-enable: true
267 | # StatViewServlet配置,说明请参考Druid Wiki,配置_StatViewServlet配置
268 | #是否启用监控界面默认值true
269 | stat-view-servlet.enabled: true
270 | # web.xml的url-pattern,也就是访问/druid/*访问到该servlet
271 | stat-view-servlet.url-pattern: /druid/*
272 | # 允许清空统计数据
273 | stat-view-servlet.reset-enable: true
274 | # 用户名
275 | stat-view-servlet.login-username: zx
276 | # 密码
277 | stat-view-servlet.login-password: 1223456
278 | # ip白名单
279 | stat-view-servlet.allow:
280 | # ip黑名单
281 | stat-view-servlet.deny:
282 | # 过滤器配置
283 | filter:
284 | stat:
285 | # 聚合sql 开启慢sql查询
286 | merge-sql: true
287 | # 是否开启慢sql查询
288 | log-slow-sql: true
289 | # 超过多少时间为慢sql 开启慢sql查询
290 | slow-sql-millis: 3000
291 | # 安全配置,防止sql注入. 具体参数可查看文档,包括禁止各类增删查改的操作
292 | # wall:
293 | # config:
294 | >
295 |
296 |
297 | #### 整合通用Mapper
298 | 1. 导入依赖:
299 |
300 |
301 | tk.mybatis
302 | mapper-spring-boot-starter
303 | ${mapper-spring-boot-starter.version}
304 |
305 |
306 |
307 | com.github.pagehelper
308 | pagehelper-spring-boot-starter
309 | ${pagehelper-spring-boot-starter.version}
310 |
311 | 2. 创建CommonMapper.java(注意。不能让@MapperScan("com.zx.springmybatis.dao")扫描到该类),其他所有mapper需要 !继承 !它。
312 |
313 | public interface CommonMapper extends Mapper,MySqlMapper {
314 | }
315 |
316 | 3. 其他mapper继承他即可。
317 |
318 | 4. 以上,除了通用mapper,pageHelper也已经可以使用(ps:startPage方法后必须紧跟查询语句;返回的PageInfo中会包含许多分页信息):
319 |
320 | public PageInfo getAllForPage(Integer pageNum, Integer pageSize) {
321 | pageNum = pageNum == null ? 1 : pageNum;
322 | pageSize = pageSize == null ? 10 : pageSize;
323 |
324 | PageHelper.startPage(pageNum,pageSize);
325 | List userList = userMapper.selectAll();
326 | PageInfo pageInfo = new PageInfo<>(userList);
327 |
328 | log.info("pageInfo:{}",pageInfo);
329 | return pageInfo;
330 | }
331 | 5. 主键回写。在主键字段上增加@GeneratedValue(generator = "JDBC")这样的注解,还有uuid等,即可回写。
332 | 该回写是在传入的实体对象中,原本为空的主键被赋值,而不是直接返回。
333 |
334 | 6. 注意:insertSelective():保存一个实体,null的属性不会保存,会使用数据库默认值;
335 | insert():保存一个实体,null的属性也会保存,不会使用数据库默认值;
336 | update的方法也是一样。带Selective的才使用默认值
337 |
338 | 7. Example使用:
339 | >
340 | Example example = new Example(User.class)//传入实体类对象构造
341 | .selectProperties("id", "name")//设置要查询的字段
342 | .excludeProperties("id");//设置不查询的字段,与要查询同时设置,要查询的优先
343 | example.orderBy("id").desc();//排序
344 | example.createCriteria();//其他方法类似,基本都能用方法名理解
345 | .andLessThan("id","4");//查询属性小于该值的记录
346 | .andGreaterThan("id","4");//查询属性大于该值的记录
347 | .andAllEqualTo(temp);//查询字段值等于该对象的属性值的记录,所有属性。
348 | .andEqualTo(temp);//查询字段值等于该对象的属性值的记录,非空属性。
349 | .andBetween("name","a","c");//between查询
350 | .andCondition("name = 'a' or name ='b'");//可以直接使用sql查询,此处输入where后面的字符
351 |
352 | List userList = userMapper.selectByExample(example);
353 | >
354 |
355 | 8. 修改操作的使用:
356 | >
357 | public void updateGradeById(Long gradeId,Grade grade) {
358 |
359 | Example example = new Example(Grade.class);
360 | example.createCriteria().andEqualTo("id", gradeId);
361 |
362 | int i = gradeMapper.updateByExampleSelective(grade, example);
363 | //根据id直接更新
364 | //gradeMapper.updateByExampleSelective();
365 | System.out.println("更新条数:" + i);
366 | }
367 | >
368 |
369 | #### 输出MyBatisSQL语句
370 | * 在yml中如下配置(com.zx.springmybatis.dao为自己的包名):
371 | >
372 | # 输出MyBatis语句,trace会输出结果,debug只输出语句
373 | logging:
374 | level:
375 | com:
376 | zx:
377 | springmybatis:
378 | dao: debug
379 | >
380 | * 或使用logback,如该博客配置:http://blog.csdn.net/qincidong/article/details/76122727
381 | 在logback.xml中配置:
382 |
383 | #### 整合MyBatisGenerator
384 | 1. 在pom.xml中添加属性如下(注释的xml,是因为不想生成xml文件,直接用注解形式的):
385 | >
386 |
387 |
388 | ${basedir}/src/main/java
389 | tk.mybatis.mapper.mapper
390 | tk.mybatis.mapper.model
391 |
392 |
393 |
394 |
395 | 3.4.4
396 | 5.1.44
397 | >
398 |
399 | 2. 增加maven插件,其参数由上面提供
400 | >
401 |
402 | org.mybatis.generator
403 | mybatis-generator-maven-plugin
404 | 1.3.5
405 |
406 | ${basedir}/src/main/resources/generator/generatorConfig.xml
407 | true
408 | true
409 |
410 |
411 |
412 | mysql
413 | mysql-connector-java
414 | ${mysql.version}
415 |
416 |
417 | tk.mybatis
418 | mapper
419 | ${mapper.version}
420 |
421 |
422 |
423 | >
424 |
425 | 3. 在resource下新增generator/generatorConfig.xml文件,其参数由下面的配置文件提供
426 |
427 | 4. 在同目录下新增config.properties文件
428 |
429 | 5. 在pom.xml这一级目录的命令行窗口执行mvn mybatis-generator:generate即可(IDEA Terminal打开可直接在该目录运行)
430 |
431 | #### MyBatis注解-动态sql的几种实现方式
432 | 1. 最原始-直接在方法注释上写动态sql代码:
433 | >
434 | @Insert("")
438 | void addAll(List grades);
439 | >
440 |
441 | 2. 使用Provider和SQL语句构建器(若不适用构建器,自己手写sql也行):
442 | 不使用SQL构建器:
443 | >
444 | /**
445 | * 使用Provider批量增加
446 | */
447 | @InsertProvider(type = Provider.class,method = "batchInsert")
448 | void addAll1(List list);
449 |
450 | /**
451 | * 使用内部类作为Provider
452 | */
453 | class Provider{
454 | /**
455 | * 返回String作为sql语句
456 | * 不使用SQL构建器
457 | * 此处的sql是原生sql
458 | *
459 | * 参数: map中存储了MyBatisMapper方法中的参数;
460 | * 如果方法只有一个参数,也可以直接写相同类型的参数直接接收;
461 | * 如果方法使用了@Param注解,则使用map用@Param的value作为key接收
462 | * 如果多个参数,且未使用@Param注解,则使用map,用索引作为key接收
463 | * 具体可以下断点自行查看map
464 | */
465 | public String batchInsert(Map map) {
466 | List list = (List) map.get("list");
467 | StringBuilder result = new StringBuilder();
468 | result.append("INSERT INTO grade(name) VALUES");
469 | list.stream().forEach(item ->{
470 | result.append("(").append("\"" + item.getName() + "\"").append(")");
471 | result.append(",");
472 | });
473 | result.deleteCharAt(result.length()-1);
474 | return result.toString();
475 | }
476 | }
477 | >
478 | 使用SQL构建器:
479 | SQL构建器使用教程(Mybatis官网): http://www.mybatis.org/mybatis-3/zh/statement-builders.html
480 | 此处不作例子了,我只能说,这个构建器构建不是批量增加等操作的sql极其方便,但如果是批量增加等sql,还不如自己拼接呢;
481 |
482 | 3. 增强型注解.
483 | * LanguageDriver接口:
484 | * createParameterHandler()方法:
485 | * 创建参数处理器,将预编译的sql的参数替换为真正内容,例如{name}/{1}这样的
486 | * createSqlSource(XNode)方法:
487 | * 创建SqlSource,它保存了从mapper.xml中读取出来的还未真正替换值的sql语句
488 | * createSqlSource(String)方法:它保存了从注解中读取出来的sql.
489 | * 该接口的实现有XMLLanguageDriver,然后xml类还有个子类是RawLanguageDriver;
490 | * XMLLanguageDriver是未解析的也就是写在xml或注解中的那样的sql.
491 | * RawLanguageDriver是解析后的,可以直接执行的原生sql.(源码注解:除非确保是原生sql,否则没有任何理由使用该类)
492 | * 自定义该接口:
493 | * 如上介绍,我们可以通过继承XMLLanguageDriver类,重写createSqlSource(String)方法来实现自己的需求;
494 | * 如下,就是我自己实现的一个通用的,可以对每个实体进行条件查询的扩展接口:
495 | >
496 | /**
497 | * author:ZhengXing
498 | * datetime:2017/11/28 0028 14:19
499 | * 通用条件查询语言驱动
500 | */
501 | public class CommonConditionLanguageDriver extends XMLLanguageDriver{
502 | /**
503 | * 重写父类方法,以便在条件查询时,将不为空属性,加入where条件,
504 | * 例如:
505 | * select * from user
506 | *
507 | * and username=#{username}
508 | * and password=#{password}
509 | *
510 | * parameterType:mapper中方法接收的参数,如果参数有多个,其值为map,当参数为多个时,无法获悉每个参数的类型(应该是)
511 | */
512 | @Override
513 | public SqlSource createSqlSource(Configuration configuration, String script, Class> parameterType) {
514 | //追加where
515 | StringBuilder sql = new StringBuilder().append("");
516 | //默认将该参数类型作为实体类类型处理,获取所有属性
517 | Field[] fields = parameterType.getDeclaredFields();
518 |
519 | //遍历实体类的每个属性
520 | for (Field field : fields) {
521 | //将java中 userId形式的属性转换为数据库中 user_id形式的
522 | String sqlField = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, field.getName());
523 | //循环增加的语句
524 | String temp = "and sqlField=#{javaField} ";
525 | //将字符串中的自定义标识字符:javaField和sqlField替换
526 | temp = temp.replaceAll("javaField",field.getName())
527 | .replaceAll("sqlField", sqlField);
528 | sql.append(temp);
529 | }
530 | sql.append("");
531 |
532 | //增加";
534 | //继续执行父类的方法实现,构建SqlSource
535 | return super.createSqlSource(configuration, script, parameterType);
536 | }
537 | }
538 | >
539 | 在Mapper中如下写法:
540 | >
541 | /**
542 | * 使用LanguageDriver进行通用的条件查询
543 | */
544 | @Lang(CommonConditionLanguageDriver.class)
545 | @Select("select * from user")
546 | List findByCondition(User user);
547 | >
548 | 如此调用,即可查询出所有 name=a,password=aa的记录,而其他空的字段则被忽略
549 | >
550 | User user = new User().setName("a").setPassword("aa");
551 | List a = userMapper.findByCondition(user);
552 | a.forEach(item-> System.out.println(a));
553 | >
554 | 当然,这类通用的sql,在通用Mapper中都已经提供了.
555 |
556 | #### 单表查询和多表关联查询的选择
557 | * 一般来说,性能是多表占优.但是如果数据量大的话或许不一定.
558 | * 多表查询如果关联表过多性能很低.
559 | * 多表查询不方便使用缓存.
560 | * 多表查询如果遇到分库分表等情况,需要重写sql
561 | * 综上所述,推荐单表查询
562 |
563 | #### SpringCache + redis 实现注解缓存
564 | 1. 引入spring redis和spring cache依赖:
565 | >
566 |
567 | org.springframework.boot
568 | spring-boot-starter-cache
569 |
570 |
571 |
572 | org.springframework.boot
573 | spring-boot-starter-data-redis
574 |
575 | >
576 |
577 | 2. 在yml如下配置即可:
578 | >
579 | #缓存
580 | cache:
581 | #缓存名字
582 | cache-names: #该属性的接收类型为list,得在这样写才可以分为一个个元素
583 | - a
584 | - b
585 | - c
586 | #缓存过期时间
587 | cacheExpires: #自定义属性,也是list,用来配置缓存过期时间
588 | - 3600
589 | - 1
590 | - 0
591 | #缓存类型,同时引入guava包和redis时,不配置可能有bug
592 | type: redis
593 | #redis配置
594 | redis:
595 | host: 106.14.7.29
596 | port: 6379
597 | password: 970389
598 | pool:
599 | max-active: 10
600 | max-idle: 1
601 | min-idle: 0
602 | max-wait: 50000
603 | >
604 |
605 | 3. 在Application类上增加:@EnableCaching注解(也就表示可用该注解一键关闭所有缓存)
606 |
607 | 4. 对所有需要缓存的对象需要实现Serializable接口
608 |
609 | 5. 此时,两次执行如下语句,第二次已经无需进行数据库查询,并且未进入方法体(其实现为AOP):
610 | !!之前我一直以为其实现是AOP...后来我在@EnableCahcing注解中找到了..Mode参数,
611 | 才发现其默认实现是代理类,当然可以选择用aop(暂未深入,但aop的实现不也是用的代理类? 猜测可能代理类模式是自己重新实现,aop模式时直接复用springFrame的aop)
612 | >
613 | /**
614 | * 查询所有班级
615 | * 注意,@Cacheable中的cacheNames值需要在yml中配置,也就是spring.cache.cache-names
616 | */
617 | @Cacheable(value = "redis")
618 | public List finAll() {
619 | log.info("查询所有班级");
620 | return gradeMapper.selectAll();
621 | }
622 | >
623 |
624 | 6. 此时如果查看redis中的key的话,会发现该程序自动缓存的所有key,都有个redis:\xac\xed\x00\x05t\x00这样的前缀,
625 | 其原因是使用了JDK默认的对象序列化方法Serializer