You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

README.md 11 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. # spring-boot-demo-orm-jdbctemplate
  2. > 本 demo 主要演示了Spring Boot如何使用 JdbcTemplate 操作数据库,并且简易地封装了一个通用的 Dao 层,包括增删改查。
  3. ## pom.xml
  4. ```xml
  5. <?xml version="1.0" encoding="UTF-8"?>
  6. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  7. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  8. <modelVersion>4.0.0</modelVersion>
  9. <artifactId>spring-boot-demo-orm-jdbctemplate</artifactId>
  10. <version>1.0.0-SNAPSHOT</version>
  11. <packaging>jar</packaging>
  12. <name>spring-boot-demo-orm-jdbctemplate</name>
  13. <description>Demo project for Spring Boot</description>
  14. <parent>
  15. <groupId>com.xkcoding</groupId>
  16. <artifactId>spring-boot-demo</artifactId>
  17. <version>1.0.0-SNAPSHOT</version>
  18. </parent>
  19. <properties>
  20. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  21. <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  22. <java.version>1.8</java.version>
  23. </properties>
  24. <dependencies>
  25. <dependency>
  26. <groupId>org.springframework.boot</groupId>
  27. <artifactId>spring-boot-starter-jdbc</artifactId>
  28. </dependency>
  29. <dependency>
  30. <groupId>org.springframework.boot</groupId>
  31. <artifactId>spring-boot-starter-web</artifactId>
  32. </dependency>
  33. <dependency>
  34. <groupId>org.springframework.boot</groupId>
  35. <artifactId>spring-boot-starter-test</artifactId>
  36. <scope>test</scope>
  37. </dependency>
  38. <dependency>
  39. <groupId>mysql</groupId>
  40. <artifactId>mysql-connector-java</artifactId>
  41. </dependency>
  42. <dependency>
  43. <groupId>cn.hutool</groupId>
  44. <artifactId>hutool-all</artifactId>
  45. </dependency>
  46. <dependency>
  47. <groupId>org.projectlombok</groupId>
  48. <artifactId>lombok</artifactId>
  49. <optional>true</optional>
  50. </dependency>
  51. </dependencies>
  52. <build>
  53. <finalName>spring-boot-demo-orm-jdbctemplate</finalName>
  54. <plugins>
  55. <plugin>
  56. <groupId>org.springframework.boot</groupId>
  57. <artifactId>spring-boot-maven-plugin</artifactId>
  58. </plugin>
  59. </plugins>
  60. </build>
  61. </project>
  62. ```
  63. ## BaseDao.java
  64. ```java
  65. /**
  66. * <p>
  67. * Dao基类
  68. * </p>
  69. *
  70. * @author yangkai.shen
  71. * @date Created in 2018-10-15 11:28
  72. */
  73. @Slf4j
  74. public class BaseDao<T, P> {
  75. private JdbcTemplate jdbcTemplate;
  76. private Class<T> clazz;
  77. @SuppressWarnings(value = "unchecked")
  78. public BaseDao(JdbcTemplate jdbcTemplate) {
  79. this.jdbcTemplate = jdbcTemplate;
  80. clazz = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
  81. }
  82. /**
  83. * 通用插入,自增列需要添加 {@link Pk} 注解
  84. *
  85. * @param t 对象
  86. * @param ignoreNull 是否忽略 null 值
  87. * @return 操作的行数
  88. */
  89. protected Integer insert(T t, Boolean ignoreNull) {
  90. String table = getTableName(t);
  91. List<Field> filterField = getField(t, ignoreNull);
  92. List<String> columnList = getColumns(filterField);
  93. String columns = StrUtil.join(Const.SEPARATOR_COMMA, columnList);
  94. // 构造占位符
  95. String params = StrUtil.repeatAndJoin("?", columnList.size(), Const.SEPARATOR_COMMA);
  96. // 构造值
  97. Object[] values = filterField.stream().map(field -> ReflectUtil.getFieldValue(t, field)).toArray();
  98. String sql = StrUtil.format("INSERT INTO {table} ({columns}) VALUES ({params})", Dict.create().set("table", table).set("columns", columns).set("params", params));
  99. log.debug("【执行SQL】SQL:{}", sql);
  100. log.debug("【执行SQL】参数:{}", JSONUtil.toJsonStr(values));
  101. return jdbcTemplate.update(sql, values);
  102. }
  103. /**
  104. * 通用根据主键删除
  105. *
  106. * @param pk 主键
  107. * @return 影响行数
  108. */
  109. protected Integer deleteById(P pk) {
  110. String tableName = getTableName();
  111. String sql = StrUtil.format("DELETE FROM {table} where id = ?", Dict.create().set("table", tableName));
  112. log.debug("【执行SQL】SQL:{}", sql);
  113. log.debug("【执行SQL】参数:{}", JSONUtil.toJsonStr(pk));
  114. return jdbcTemplate.update(sql, pk);
  115. }
  116. /**
  117. * 通用根据主键更新,自增列需要添加 {@link Pk} 注解
  118. *
  119. * @param t 对象
  120. * @param pk 主键
  121. * @param ignoreNull 是否忽略 null 值
  122. * @return 操作的行数
  123. */
  124. protected Integer updateById(T t, P pk, Boolean ignoreNull) {
  125. String tableName = getTableName(t);
  126. List<Field> filterField = getField(t, ignoreNull);
  127. List<String> columnList = getColumns(filterField);
  128. List<String> columns = columnList.stream().map(s -> StrUtil.appendIfMissing(s, " = ?")).collect(Collectors.toList());
  129. String params = StrUtil.join(Const.SEPARATOR_COMMA, columns);
  130. // 构造值
  131. List<Object> valueList = filterField.stream().map(field -> ReflectUtil.getFieldValue(t, field)).collect(Collectors.toList());
  132. valueList.add(pk);
  133. Object[] values = ArrayUtil.toArray(valueList, Object.class);
  134. String sql = StrUtil.format("UPDATE {table} SET {params} where id = ?", Dict.create().set("table", tableName).set("params", params));
  135. log.debug("【执行SQL】SQL:{}", sql);
  136. log.debug("【执行SQL】参数:{}", JSONUtil.toJsonStr(values));
  137. return jdbcTemplate.update(sql, values);
  138. }
  139. /**
  140. * 通用根据主键查询单条记录
  141. *
  142. * @param pk 主键
  143. * @return 单条记录
  144. */
  145. public T findOneById(P pk) {
  146. String tableName = getTableName();
  147. String sql = StrUtil.format("SELECT * FROM {table} where id = ?", Dict.create().set("table", tableName));
  148. RowMapper<T> rowMapper = new BeanPropertyRowMapper<>(clazz);
  149. log.debug("【执行SQL】SQL:{}", sql);
  150. log.debug("【执行SQL】参数:{}", JSONUtil.toJsonStr(pk));
  151. return jdbcTemplate.queryForObject(sql, new Object[]{pk}, rowMapper);
  152. }
  153. /**
  154. * 根据对象查询
  155. *
  156. * @param t 查询条件
  157. * @return 对象列表
  158. */
  159. public List<T> findByExample(T t) {
  160. String tableName = getTableName(t);
  161. List<Field> filterField = getField(t, true);
  162. List<String> columnList = getColumns(filterField);
  163. List<String> columns = columnList.stream().map(s -> " and " + s + " = ? ").collect(Collectors.toList());
  164. String where = StrUtil.join(" ", columns);
  165. // 构造值
  166. Object[] values = filterField.stream().map(field -> ReflectUtil.getFieldValue(t, field)).toArray();
  167. String sql = StrUtil.format("SELECT * FROM {table} where 1=1 {where}", Dict.create().set("table", tableName).set("where", StrUtil.isBlank(where) ? "" : where));
  168. log.debug("【执行SQL】SQL:{}", sql);
  169. log.debug("【执行SQL】参数:{}", JSONUtil.toJsonStr(values));
  170. List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql, values);
  171. List<T> ret = CollUtil.newArrayList();
  172. maps.forEach(map -> ret.add(BeanUtil.fillBeanWithMap(map, ReflectUtil.newInstance(clazz), true, false)));
  173. return ret;
  174. }
  175. /**
  176. * 获取表名
  177. *
  178. * @param t 对象
  179. * @return 表名
  180. */
  181. private String getTableName(T t) {
  182. Table tableAnnotation = t.getClass().getAnnotation(Table.class);
  183. if (ObjectUtil.isNotNull(tableAnnotation)) {
  184. return StrUtil.format("`{}`", tableAnnotation.name());
  185. } else {
  186. return StrUtil.format("`{}`", t.getClass().getName().toLowerCase());
  187. }
  188. }
  189. /**
  190. * 获取表名
  191. *
  192. * @return 表名
  193. */
  194. private String getTableName() {
  195. Table tableAnnotation = clazz.getAnnotation(Table.class);
  196. if (ObjectUtil.isNotNull(tableAnnotation)) {
  197. return StrUtil.format("`{}`", tableAnnotation.name());
  198. } else {
  199. return StrUtil.format("`{}`", clazz.getName().toLowerCase());
  200. }
  201. }
  202. /**
  203. * 获取列
  204. *
  205. * @param fieldList 字段列表
  206. * @return 列信息列表
  207. */
  208. private List<String> getColumns(List<Field> fieldList) {
  209. // 构造列
  210. List<String> columnList = CollUtil.newArrayList();
  211. for (Field field : fieldList) {
  212. Column columnAnnotation = field.getAnnotation(Column.class);
  213. String columnName;
  214. if (ObjectUtil.isNotNull(columnAnnotation)) {
  215. columnName = columnAnnotation.name();
  216. } else {
  217. columnName = field.getName();
  218. }
  219. columnList.add(StrUtil.format("`{}`", columnName));
  220. }
  221. return columnList;
  222. }
  223. /**
  224. * 获取字段列表 {@code 过滤数据库中不存在的字段,以及自增列}
  225. *
  226. * @param t 对象
  227. * @param ignoreNull 是否忽略空值
  228. * @return 字段列表
  229. */
  230. private List<Field> getField(T t, Boolean ignoreNull) {
  231. // 获取所有字段,包含父类中的字段
  232. Field[] fields = ReflectUtil.getFields(t.getClass());
  233. // 过滤数据库中不存在的字段,以及自增列
  234. List<Field> filterField;
  235. Stream<Field> fieldStream = CollUtil.toList(fields).stream().filter(field -> ObjectUtil.isNull(field.getAnnotation(Ignore.class)) || ObjectUtil.isNull(field.getAnnotation(Pk.class)));
  236. // 是否过滤字段值为null的字段
  237. if (ignoreNull) {
  238. filterField = fieldStream.filter(field -> ObjectUtil.isNotNull(ReflectUtil.getFieldValue(t, field))).collect(Collectors.toList());
  239. } else {
  240. filterField = fieldStream.collect(Collectors.toList());
  241. }
  242. return filterField;
  243. }
  244. }
  245. ```
  246. ## application.yml
  247. ```yaml
  248. server:
  249. port: 8080
  250. servlet:
  251. context-path: /demo
  252. spring:
  253. datasource:
  254. url: jdbc:mysql://127.0.0.1:3306/spring-boot-demo?useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&failOverReadOnly=false&serverTimezone=GMT%2B8
  255. username: root
  256. password: root
  257. driver-class-name: com.mysql.cj.jdbc.Driver
  258. type: com.zaxxer.hikari.HikariDataSource
  259. initialization-mode: always
  260. continue-on-error: true
  261. schema:
  262. - "classpath:db/schema.sql"
  263. data:
  264. - "classpath:db/data.sql"
  265. hikari:
  266. minimum-idle: 5
  267. connection-test-query: SELECT 1 FROM DUAL
  268. maximum-pool-size: 20
  269. auto-commit: true
  270. idle-timeout: 30000
  271. pool-name: SpringBootDemoHikariCP
  272. max-lifetime: 60000
  273. connection-timeout: 30000
  274. logging:
  275. level:
  276. com.xkcoding: debug
  277. ```
  278. ## 备注
  279. 其余详细代码参见 demo