出来了select java application_Spring项目里将SQL语句写在.sql文件中的方法

前言

我们在使用 JDBC 时, 如果把所有的 SQL 语句全写在 Java 文件中, 由于 Java 不支持 Here Document, 多行字符串要么用加号, 要么用 Java 8 的 String.join()方法来连接, 同时不能对 SQL 语句进行语法加亮, 所以这样的 SQL 字符串阅读性很差. 别说为何不用 Hibernate 之类的而不直接写原始的 SQL 语句, 在操作复杂的系统时还是会用到 JdbcTemplate 吧.

所以我们希望能把 SQL 语句写在单独的 *.sql 文件里, 这样很多编辑器就能语法高亮显示, 或在输入时还能得到智能提示.

有种办法是把 *.sql 用作为属性文件, 那么在其中定义多行的 SQL 语句时就得这样

select.user=select id, firstname, lastname, address \

from users \

where id=?

加载后就能用 getProperty("select.user")来引用相应的语句了. 属性文件的换行与 Bash 一样, 也是用 \, 但如此, 则 *.sql 并非一个纯粹的 SQL 文件, 不能正确的进行语法加亮, 一旦写上 SQL 的注释 -- 就更是在添乱了.

所以我们的第二个方案是: 首先 *.sql 就该是一个真正的 SQL 文件, 而不是伪装的属性文件, 为了能在程序中引用每一条 SQL 语句, 我们该如何表示各自的 Key 呢? 这里的灵感仍然是来自于 Linux Shell, 在 Linux Shell 中指定执行环境的用了特殊的注释方式 #!, 如

#!/bin/bash

#!/usr/bin/env python

依葫芦画瓢, SQL 的标准单注释是 --, 因而我们也创建一个特别的注释 --!, , 其后的字符串就是接下来 SQL 语句的 Key.

举例如下

--!select.user

select id, firstname, lastname, address

from users

where id=?

--!update.user

update ........

--! 之后是 key select.user, 往下在未到文件结束, 或是遇到下一个 --! 之前就是这个 key 对应的完整 SQL 语句的内容.

本文以 Spring 项目为例来演示如何应这个 SQL 文件, 其实在其他类型的 Java 项目中同样可以借鉴.

因为这是一个真正的 SQL 文件, 所以在 Spring 中我们无法直接作为属性文件来加载. 假设我们把该文件存储为 src/resources/sql/queries.sql, 因此我们不能直接用

@PropertySource(value = "classpath:sql/queries.sql")

public class AppConfig { ...... }

加载该文件.

幸好 PropertySource 注解还有一个属性 factory, 类型为 PropertySourceFactory, 这就是我们作文章的地方, 马上着手自定义一个 SqlPropertySourceFactory, 在其中总有办法把一个 *.sql 的内容转换为 Properties. 因此将来我们要加载 sql/queries.sql 文件所用的注解形式就会是

@PropertySource(value = "classpath:sql/queries.sql", factory = SqlPropertySourceFactory.class)

public class AppConfig { ......}

接下来就是本文的关键, 看看 SqlPropertySourceFactory 的实现

SqlPropertySourceFactory.java

package cc.unmi;

import org.springframework.core.env.MapPropertySource;

import org.springframework.core.env.PropertySource;

import org.springframework.core.io.support.EncodedResource;

import org.springframework.core.io.support.PropertySourceFactory;

import java.io.BufferedReader;

import java.io.IOException;

import java.util.*;

import java.util.stream.Collectors;

public class SqlPropertySourceFactory implements PropertySourceFactory {

private static final String KEY_LEADING = "--!";

@Override

public PropertySource> createPropertySource(String name, EncodedResource resource) throws IOException {

Deque queries = new LinkedList<>();

new BufferedReader(resource.getReader()).lines().forEach(line -> {

if (line.startsWith(KEY_LEADING)) {

queries.addLast(new Pair(line.replaceFirst(KEY_LEADING, "")));

} else if (line.startsWith("--")) {

//skip comment line

} else if (!line.trim().isEmpty()) {

Optional.ofNullable(queries.getLast()).ifPresent(pair -> pair.lines.add(line));

}

});

Map sqlMap = queries.stream()

.filter(pair -> !pair.lines.isEmpty())

.collect(Collectors.toMap(pair -> pair.key,

pair -> String.join(System.lineSeparator(), pair.lines),

(r, pair) -> r, LinkedHashMap::new));

System.out.println("Configured SQL statements:");

sqlMap.forEach((s, o) -> System.out.println(s + "=" + o));

return new MapPropertySource(resource.toString(), sqlMap);

}

private static class Pair {

private String key;

private List lines = new LinkedList<>();

Pair(String key) {

this.key = key;

}

}

}

我们定义的 src/resources/sql/queries.sql 文件内容如下:

--external queries in this file

--!select_users_by_id

select id, firstname, lastname, address

from users where id=?

--!add_user

insert users(id, firstname, lastname, address)

values(DEFAULT, ?, ?, ?)

--

--!no_statement

---

--!update

update users set firstname=? where id=?

最后是如何应用它, 我们以 SpringBoot 的方式来启动一个 Spring 项目

DemoApplication.java

package cc.unmi;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.context.EnvironmentAware;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.PropertySource;

import org.springframework.core.env.Environment;

@SpringBootApplication

@PropertySource(value = "classpath:sql/queries.sql", factory = SqlPropertySourceFactory.class)

public class DemoApplication implements EnvironmentAware {

private Environment env;

@Value("${add_user}")

private String sqlAddUser;

@Bean

public String testBean() {

System.out.println("SQL_1:" + env.getProperty("select_users_by_id"));

System.out.println("SQL_2:" + sqlAddUser);

return "testBean";

}

public static void main(String[] args) {

SpringApplication.run(DemoApplication.class, args);

}

@Override

public void setEnvironment(Environment environment) {

env = environment;

}

}

既然已转换为普通的属性了, 所以可以通过表达式 ${key}或 env.getProperty("key")来引用它们.

执行上面的代码, 输出如下:

Configured SQL statements:

select_users_by_id=select id, firstname, lastname, address

from users where id=?

add_user=insert users(id, firstname, lastname, address)

values(DEFAULT, ?, ?, ?)

update=update users set firstname=? where id=?

SQL_1:select id, firstname, lastname, address

from users where id=?

SQL_2:insert users(id, firstname, lastname, address)

values(DEFAULT, ?, ?, ?)

就这么简单. 当然那个 *.sql 文件最好是写得严谨一些, 我们可以将来对 SqlPropertySourceFactory 进行逐步完善以应对更多的可能. 不管怎么说它是一个真正的 SQL 文件, 在代码中也能像任何别的属性那么方便的引用其中定义的 SQL 语句了.

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot应用,可以通过在application.yml文件配置,实现打印JDBC的SQL语句的功能。 具体的实现步骤如下: 1. 在application.yml文件,配置以下信息: ```yaml spring: datasource: url: jdbc:mysql://localhost:3306/test username: root password: root driver-class-name: com.mysql.jdbc.Driver # 打印SQL语句 # 可选的值:NONE、CONSOLE、LOG_FILE、LOG_FILE_AND_CONSOLE # 默认值:NONE # 可以使用logging.level.org.springframework.jdbc.core.JdbcTemplate=DEBUG代替 #logging: # level: # org: # springframework: # jdbc: # core: # JdbcTemplate: DEBUG datasource: type: com.alibaba.druid.pool.DruidDataSource # 配置Druid数据源,可以省略 druid: initial-size: 5 max-active: 20 min-idle: 5 max-wait: 60000 pool-prepared-statements: true max-pool-prepared-statement-per-connection-size: 20 validation-query: SELECT 1 test-on-borrow: true test-while-idle: true time-between-eviction-runs-millis: 60000 min-evictable-idle-time-millis: 300000 filters: stat,wall,log4j connection-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 ``` 其,logging.level.org.springframework.jdbc.core.JdbcTemplate=DEBUG可以在application.yml配置,也可以在log4j或logback等日志框架配置。 2. 在代码使用JdbcTemplate时,即可在日志看到打印的SQL语句,例如: ```java @Autowired private JdbcTemplate jdbcTemplate; public void query() { List<Map<String, Object>> result = jdbcTemplate.queryForList("SELECT * FROM user"); //... } ``` 在日志会打印出类似以下的SQL语句: ```java DEBUG [main] o.s.j.c.JdbcTemplate - Executing SQL query [SELECT * FROM user] ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值