├── .gitignore
├── README.md
├── pom.xml
└── src
├── main
├── java
│ └── com
│ │ └── zx
│ │ └── springmybatis
│ │ ├── SpringMyBatisApplication.java
│ │ ├── config
│ │ ├── cache
│ │ │ ├── CustomRedisCacheExpireProperties.java
│ │ │ └── RedisCacheConfig.java
│ │ ├── druid
│ │ │ ├── DruidDataSourceConfiguration.java
│ │ │ ├── DruidStatFilter.java
│ │ │ └── DruidStatViewServlet.java
│ │ └── mybatis
│ │ │ └── CommonMapper.java
│ │ ├── controller
│ │ └── TestController.java
│ │ ├── dao
│ │ ├── GradeMapper.java
│ │ ├── UserMapper.java
│ │ └── driver
│ │ │ └── CommonConditionLanguageDriver.java
│ │ ├── dto
│ │ └── GradeDTO.java
│ │ ├── entity
│ │ ├── Grade.java
│ │ └── User.java
│ │ └── service
│ │ ├── UserService.java
│ │ └── impl
│ │ ├── CacheService.java
│ │ └── UserServiceImpl.java
└── resources
│ ├── application.yml
│ ├── generator
│ ├── config.properties
│ └── generatorConfig.xml
│ ├── logback.xml
│ ├── mapper
│ └── User.xml
│ └── sql
│ └── test1.sql
└── test
└── java
└── com
└── zx
└── springmybatis
├── SpringMyBatisApplicationTests.java
├── dao
└── UserMapperTest.java
└── service
└── impl
└── CacheServiceTest.java
/.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/
--------------------------------------------------------------------------------
/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