对于使用Mockito模拟JdbcTemplate的数据源感兴趣的读者,本文将会是一篇不错的选择,我们将详细介绍mockjdbc,并为您提供关于20.SpringBoot默认、自定义数据源、配置多个数
对于使用Mockito模拟JdbcTemplate的数据源感兴趣的读者,本文将会是一篇不错的选择,我们将详细介绍mock jdbc,并为您提供关于20. Spring Boot 默认、自定义数据源 、配置多个数据源 jdbcTemplate操作DB、common-jdbc:一个基于SpringJdbcTemplate的高性能数据库操作工具类库、JDBC + Spring JDBC(JdbcTemplate)、jdbc-plus是一款基于JdbcTemplate增强工具包, 基于JdbcTemplate已实现分页、多租户等插件,可自定义扩展插件的有用信息。
本文目录一览:- 使用Mockito模拟JdbcTemplate的数据源(mock jdbc)
- 20. Spring Boot 默认、自定义数据源 、配置多个数据源 jdbcTemplate操作DB
- common-jdbc:一个基于SpringJdbcTemplate的高性能数据库操作工具类库
- JDBC + Spring JDBC(JdbcTemplate)
- jdbc-plus是一款基于JdbcTemplate增强工具包, 基于JdbcTemplate已实现分页、多租户等插件,可自定义扩展插件
使用Mockito模拟JdbcTemplate的数据源(mock jdbc)
我正在尝试在Spring项目中测试一个类。我想在测试类和dao类中进行尽可能多的更改,这样我就不必因为更改而重新测试所有类型的东西。
我正在使用的JdbcTemplate template
类具有通过以下实例化的类变量:
setJdbcTemplate(DataSource dataSource) { this.template = new JdbcTemplate(dataSource);}
我想测试的方法使a template.query(<code>)
运行定义的SQL查询并将结果返回到列表。
我在测试用例中创建了以下内容,但不确定如何使用它。我可以使用Mockito使以下代码返回特定的字符串列表吗?
DataSource mockedDataSrc = Mockito.mock(DataSource.class);customerClassDao.setJdbcTemplate(mockedDataSrc);
我可以以某种方式使用when
或其他命令来设置要返回给JdbcTemplate .query
调用的内容吗?
答案1
小编典典您无法执行此操作,因为您无法控制JdbcTemplate
实现。您应该依赖注入JdbcTemplate
,然后模拟JdbcTemplate
。
这个困难指出了您的代码有问题。您的代码取决于的具体实例JdbcTemplate
。如果您对它使用依赖注入,则耦合度会降低。
由于您不想修改被测系统,因此可以执行以下操作:
更改template
字段,使其受到程序包保护(即:删除private关键字)。然后,在实例化要测试的类之后,将其设置为模拟(JdbcTemplate.class)。现在,您将能够像最初想要的那样直接使用when并在JdbcTemplate上进行验证。
因此,您正在测试的类将如下所示:
public class SystemUnderTest {JdbcTemplate jdbcTemplate; public void setJdbcTemplate(DataSource dataSource) { this.template = new JdbcTemplate(dataSource); }}
您的测试将执行此操作:
@Beforepublic void setUp() { SystemUnderTest sut = new SystemUnderTest(); sut.jdbcTemplate = mock(JdbcTemplate.class); }// ...
20. Spring Boot 默认、自定义数据源 、配置多个数据源 jdbcTemplate操作DB
Spring-Boot-2.0.0-M1版本将默认的数据库连接池从tomcat jdbc pool改为了hikari,这里主要研究下hikari的默认配置
0. 创建Spring Boot项目,选中 Web、MySQL、JDBC 依赖
1. 启动类默认加载了DataSourceAutoConfiguration,默认数据源是HikariDataSource
@SpringBootApplicationpublic class SpringBootJdbcApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootJdbcApplication.class, args);
}
}
2. 数据源配置文件属性
spring:
datasource:
url: jdbc:mysql://localhost:3306/springboot //系统自带的默认数据源配置中,用 url,若是自定义的HikariDataSource,用jdbcUrl (自定义的其它类型数据源名称取什么待测)
username: root
password: xiaochao
driver-class-name: com.mysql.cj.jdbc.Driver //老版本用com.mysql.jdbc.Driver
name: hikari
3. 测试
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringBootJdbcApplicationTests {
@Autowired
DataSource dataSource;
@Test
public void contextLoads() throws SQLException {
System.out.println(dataSource.getClass().getName());
System.out.println(dataSource.getConnection());
}
}
结果:
HikariProxyConnection@265939934 wrapping com.mysql.cj.jdbc.ConnectionImpl@14982a82
com.zaxxer.hikari.HikariDataSource
4. 自定义数据源
4.1 首先排除掉系统默认数据源
- exclude:排除,不包括,祛除,赶出,反义词 : include
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})public class SpringBootJdbcApplication { public static void main(String[] args) { SpringApplication.run(SpringBootJdbcApplication.class, args); } }
4.2 在配置类中加入自定义的线程池
-
@Bean @ConfigurationProperties("spring.datasource") //此配置项名称是自定义的,只要和配置文件中的各项名称对应上就好 public HikariDataSource dataSource() { return DataSourceBuilder.create().type(HikariDataSource.class).build(); }
-
4.3 把配置文件中的spring.datasource.url 改为spring.datasource.jdbcUrl
- 4.4 自动以tomcat jdbc 数据源 首先引入tomcat-jdbc依赖
-
<dependency> <groupId>org.apache.tomcat</groupId> <artifactId>tomcat-jdbc</artifactId> <version>9.0.13</version> </dependency>
-
配置tomcat数据源Bean
@Configuration
public class MyConfig {
@Bean
@ConfigurationProperties("spring.datasource") //此配置项名称是自定义的,只要能和配置文件中的各项名称对应上就好
public org.apache.tomcat.jdbc.pool.DataSource dataSource() {
return DataSourceBuilder.create().type(org.apache.tomcat.jdbc.pool.DataSource.class).build();
}
}
自定义其它类型的数据源方式同理
配置多个数据源
5. 数据源自动配置 源码解析
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.springframework.boot.autoconfigure.jdbc;
import com.zaxxer.hikari.HikariDataSource;
import javax.sql.DataSource;
import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DatabaseDriver;
import org.springframework.context.annotation.Bean;
import org.springframework.util.StringUtils;
abstract class DataSourceConfiguration {
DataSourceConfiguration() {
}
protected static <T> T createDataSource(DataSourceProperties properties, Class<? extends DataSource> type) {
return properties.initializeDataSourceBuilder().type(type).build();
}
@ConditionalOnMissingBean({DataSource.class})
@ConditionalOnProperty(
name = {"spring.datasource.type"}
)
static class Generic {
Generic() {
}
@Bean
public DataSource dataSource(DataSourceProperties properties) {
return properties.initializeDataSourceBuilder().build();
}
}
@ConditionalOnClass({BasicDataSource.class})
@ConditionalOnMissingBean({DataSource.class})
@ConditionalOnProperty(
name = {"spring.datasource.type"},
havingValue = "org.apache.commons.dbcp2.BasicDataSource",
matchIfMissing = true
)
static class Dbcp2 {
Dbcp2() {
}
@Bean
@ConfigurationProperties(
prefix = "spring.datasource.dbcp2"
)
public BasicDataSource dataSource(DataSourceProperties properties) {
return (BasicDataSource)DataSourceConfiguration.createDataSource(properties, BasicDataSource.class);
}
}
@ConditionalOnClass({HikariDataSource.class})
@ConditionalOnMissingBean({DataSource.class})
@ConditionalOnProperty(
name = {"spring.datasource.type"},
havingValue = "com.zaxxer.hikari.HikariDataSource",
matchIfMissing = true
)
static class Hikari {
Hikari() {
}
@Bean
@ConfigurationProperties(
prefix = "spring.datasource.hikari"
)
public HikariDataSource dataSource(DataSourceProperties properties) {
HikariDataSource dataSource = (HikariDataSource)DataSourceConfiguration.createDataSource(properties, HikariDataSource.class);
if (StringUtils.hasText(properties.getName())) {
dataSource.setPoolName(properties.getName());
}
return dataSource;
}
}
@ConditionalOnClass({org.apache.tomcat.jdbc.pool.DataSource.class})
@ConditionalOnMissingBean({DataSource.class})
@ConditionalOnProperty(
name = {"spring.datasource.type"},
havingValue = "org.apache.tomcat.jdbc.pool.DataSource",
matchIfMissing = true
)
static class Tomcat {
Tomcat() {
}
@Bean
@ConfigurationProperties(
prefix = "spring.datasource.tomcat"
)
public org.apache.tomcat.jdbc.pool.DataSource dataSource(DataSourceProperties properties) {
org.apache.tomcat.jdbc.pool.DataSource dataSource = (org.apache.tomcat.jdbc.pool.DataSource)DataSourceConfiguration.createDataSource(properties, org.apache.tomcat.jdbc.pool.DataSource.class);
DatabaseDriver databaseDriver = DatabaseDriver.fromJdbcUrl(properties.determineUrl());
String validationQuery = databaseDriver.getValidationQuery();
if (validationQuery != null) {
dataSource.setTestOnBorrow(true);
dataSource.setValidationQuery(validationQuery);
}
return dataSource;
}
}
}
参考文档:Spring Boot 官方文档 —— 84.1 Configure a Custom DataSource 章节
package org.springframework.boot.autoconfigure.jdbc;
@Configuration
@ConditionalOnClass({DataSource.class, EmbeddedDatabaseType.class})
@EnableConfigurationProperties({DataSourceProperties.class})
@Import({DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class})
public class DataSourceAutoConfiguration {
public DataSourceAutoConfiguration() {}
。。。。。。
}
@Configuration
@Import({DataSourceInitializerInvoker.class, DataSourceInitializationConfiguration.Registrar.class})
class DataSourceInitializationConfiguration {
DataSourceInitializationConfiguration() {
}
static class Registrar implements ImportBeanDefinitionRegistrar {
private static final String BEAN_NAME = "dataSourceInitializerPostProcessor";
Registrar() {
}
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
if (!registry.containsBeanDefinition("dataSourceInitializerPostProcessor")) {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(DataSourceInitializerPostProcessor.class);
beanDefinition.setRole(2);
beanDefinition.setSynthetic(true);
registry.registerBeanDefinition("dataSourceInitializerPostProcessor", beanDefinition);
}
}
}
}
package org.springframework.boot.autoconfigure.jdbc;
import javax.sql.DataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener; //此类是一个监听器
class DataSourceInitializerInvoker implements ApplicationListener<DataSourceSchemaCreatedEvent>, InitializingBean {
private static final Log logger = LogFactory.getLog(DataSourceInitializerInvoker.class);
private final ObjectProvider<DataSource> dataSource;
private final DataSourceProperties properties;
private final ApplicationContext applicationContext;
private DataSourceInitializer dataSourceInitializer;
private boolean initialized;
DataSourceInitializerInvoker(ObjectProvider<DataSource> dataSource, DataSourceProperties properties, ApplicationContext applicationContext) {
this.dataSource = dataSource;
this.properties = properties;
this.applicationContext = applicationContext;
}
public void afterPropertiesSet() {
DataSourceInitializer initializer = this.getDataSourceInitializer();
if (initializer != null) {
boolean schemaCreated = this.dataSourceInitializer.createSchema(); //run DDL schema
if (schemaCreated) {
this.initialize(initializer);
}
}
}
private void initialize(DataSourceInitializer initializer) {
try {
this.applicationContext.publishEvent(new DataSourceSchemaCreatedEvent(initializer.getDataSource()));
if (!this.initialized) {
this.dataSourceInitializer.initSchema(); //run init data schema
this.initialized = true;
}
} catch (IllegalStateException var3) {
logger.warn("Could not send event to complete DataSource initialization (" + var3.getMessage() + ")");
}
}
//监听器监听到事件后运行的方法
public void onApplicationEvent(DataSourceSchemaCreatedEvent event) {
DataSourceInitializer initializer = this.getDataSourceInitializer();
if (!this.initialized && initializer != null) {
initializer.initSchema(); //run init data schema
this.initialized = true;
}
}
private DataSourceInitializer getDataSourceInitializer() {
if (this.dataSourceInitializer == null) {
DataSource ds = (DataSource)this.dataSource.getIfUnique();
if (ds != null) {
this.dataSourceInitializer = new DataSourceInitializer(ds, this.properties, this.applicationContext);
}
}
return this.dataSourceInitializer;
}
}
package org.springframework.boot.autoconfigure.jdbc;
import ......;
class DataSourceInitializer { //【初始化程序】
private static final Log logger = LogFactory.getLog(DataSourceInitializer.class);
private final DataSource dataSource;
private final DataSourceProperties properties;
private final ResourceLoader resourceLoader;
DataSourceInitializer(DataSource dataSource, DataSourceProperties properties, ResourceLoader resourceLoader) {
this.dataSource = dataSource;
this.properties = properties;
this.resourceLoader = (ResourceLoader)(resourceLoader != null ? resourceLoader : new DefaultResourceLoader());
}
DataSourceInitializer(DataSource dataSource, DataSourceProperties properties) {
this(dataSource, properties, (ResourceLoader)null);
}
public DataSource getDataSource() {
return this.dataSource;
}
//建表SQL,classpath*:schema-*.sql
public boolean createSchema() {
//this.properties.getSchema()即:用户在配置文件中指定的 spring.datasource.schema=xxx
List<Resource> scripts = this.getScripts("spring.datasource.schema", this.properties.getSchema(), "schema");
if (!scripts.isEmpty()) {
if (!this.isEnabled()) {
logger.debug("Initialization disabled (not running DDL scripts)");
return false;
}
String username = this.properties.getSchemaUsername();
String password = this.properties.getSchemaPassword();
this.runScripts(scripts, username, password);
}
return !scripts.isEmpty();
}
//初始化数据SQL ,classpath*:data-*.sql
public void initSchema() {
//this.properties.getData 即:用户在配置文件中自定义的:spring.datasource.data=XXX
List<Resource> scripts = this.getScripts("spring.datasource.data", this.properties.getData(), "data");
if (!scripts.isEmpty()) {
if (!this.isEnabled()) {
logger.debug("Initialization disabled (not running data scripts)");
return;
}
String username = this.properties.getDataUsername();
String password = this.properties.getDataPassword();
this.runScripts(scripts, username, password);
}
}
。。。。。。
//根据特定名称规则获取SQL脚本文件
private List<Resource> getScripts(String propertyName, List<String> resources, String fallback) {
if (resources != null) { //指定名称的脚本文件名字集合
return this.getResources(propertyName, resources, true); //从指定名称的脚本名字文件集合中获取脚本SQL
} else { //若是没有指定的,则在类路径下寻找通配规则的脚本文件
String platform = this.properties.getPlatform(); //平台,纲领
List<String> fallbackResources = new ArrayList();
//这里为啥add() 2个 SQL?
fallbackResources.add("classpath*:" + fallback + "-" + platform + ".sql"); //DatasourceProperties中的构造器中,platform默认被赋值为 all
fallbackResources.add("classpath*:" + fallback + ".sql");
return this.getResources(propertyName, fallbackResources, false);
}
}
。。。。。。
//运行SQL脚本
private void runScripts(List<Resource> resources, String username, String password) {
if (!resources.isEmpty()) {
ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
populator.setContinueOnError(this.properties.isContinueOnError());
populator.setSeparator(this.properties.getSeparator());
if (this.properties.getSqlScriptEncoding() != null) {
populator.setSqlScriptEncoding(this.properties.getSqlScriptEncoding().name());
}
Iterator var5 = resources.iterator();
while(var5.hasNext()) {
Resource resource = (Resource)var5.next();
populator.addScript(resource);
}
DataSource dataSource = this.dataSource;
if (StringUtils.hasText(username) && StringUtils.hasText(password)) {
dataSource = DataSourceBuilder.create(this.properties.getClassLoader()).driverClassName(this.properties.determineDriverClassName()).url(this.properties.determineUrl()).username(username).password(password).build();
}
DatabasePopulatorUtils.execute(populator, dataSource);
}
}
}
package org.springframework.boot.autoconfigure.jdbc;
@ConfigurationProperties(
prefix = "spring.datasource"
)
public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean {private Class<? extends DataSource> type;
private String driverClassName;
private String url;
private String username;
private String password;private DataSourceInitializationMode initializationMode;
private String platform;
private List<String> schema;private Charset sqlScriptEncoding;
......public DataSourceProperties() {
this.initializationMode = DataSourceInitializationMode.EMBEDDED;
this.platform = "all";
this.continueOnError = false;
this.separator = ";";
this.embeddedDatabaseConnection = EmbeddedDatabaseConnection.NONE;
this.xa = new DataSourceProperties.Xa();
}
}
通配规则:schema‐*.sql、data‐*.sql, 优先级第三高 eg:schema-emp.sql, schema-dept.sql; data-emp.sql ,data-dept.sql
默认规则:schema.sql,schema‐all.sql (或者关系,给用户多个选择); 优先级第二高,
自定义规则:yml中配置,根据上面代码,优先级高
spring:
datasource:
schema:
‐ classpath: department.sql
‐ classpath: user.sql
......
data:
‐ classpath: department_initData.sql
‐ classpath: user_initData.sql
......
自定义指定的SQL文件
经过测验,schema.sql 和schema-all.sql 在resources目录下并没有生效,有待继续查验
6. jdbcTemplate操作数据库
@Controller
@RequestMapping("/user")
public class UserController {
@Autowired
JdbcTemplate jdbcTemplate;
@ResponseBody
@RequestMapping("/getById2/{id}")
public List<Map<String, Object>> selectByPrimaryKey2(@PathVariable Integer id){
//这种query is OK
List<Map<String, Object>> maps = jdbcTemplate.queryForList("SELECT * FROM USER");
return maps;
//以下几种方式都会产生error,error信息见下文:待查明
// Object[] obj = new Object[1];
// obj[0] = id;
// return jdbcTemplate.queryForList("select * from user where id = ?", obj,User.class);
// return jdbcTemplate.queryForList("SELECT * FROM USER", User.class);
// return jdbcTemplate.queryForObject("select id,name,sex,age from user where id = " + id,User.class);
}
}
org.springframework.jdbc.IncorrectResultSetColumnCountException: Incorrect column count: expected 1, actual 4
at org.springframework.jdbc.core.SingleColumnRowMapper.mapRow(SingleColumnRowMapper.java:110) ~[spring-jdbc-5.1.3.RELEASE.jar:5.1.3.RELEASE]
at org.springframework.jdbc.core.RowMapperResultSetExtractor.extractData(RowMapperResultSetExtractor.java:94) ~[spring-jdbc-5.1.3.RELEASE.jar:5.1.3.RELEASE]
at org.springframework.jdbc.core.RowMapperResultSetExtractor.extractData(RowMapperResultSetExtractor.java:61) ~[spring-jdbc-5.1.3.RELEASE.jar:5.1.3.RELEASE]
at org.springframework.jdbc.core.JdbcTemplate$1QueryStatementCallback.doInStatement(JdbcTemplate.java:440) ~[spring-jdbc-5.1.3.RELEASE.jar:5.1.3.RELEASE]
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:376) ~[spring-jdbc-5.1.3.RELEASE.jar:5.1.3.RELEASE]
at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:452) ~[spring-jdbc-5.1.3.RELEASE.jar:5.1.3.RELEASE]
at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:462) ~[spring-jdbc-5.1.3.RELEASE.jar:5.1.3.RELEASE]
at org.springframework.jdbc.core.JdbcTemplate.queryForList(JdbcTemplate.java:485) ~[spring-jdbc-5.1.3.RELEASE.jar:5.1.3.RELEASE]
at com.everjiankang.cache.controller.UserController.selectByPrimaryKey2(UserController.java:53) ~[classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_181]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_181]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_181]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_181]
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:189) ~[spring-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138) ~[spring-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102) ~[spring-webmvc-5.1.3.RELEASE.jar:5.1.3.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895) ~[spring-webmvc-5.1.3.RELEASE.jar:5.1.3.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:800) ~[spring-webmvc-5.1.3.RELEASE.jar:5.1.3.RELEASE]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.1.3.RELEASE.jar:5.1.3.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1038) ~[spring-webmvc-5.1.3.RELEASE.jar:5.1.3.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942) ~[spring-webmvc-5.1.3.RELEASE.jar:5.1.3.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005) ~[spring-webmvc-5.1.3.RELEASE.jar:5.1.3.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:897) ~[spring-webmvc-5.1.3.RELEASE.jar:5.1.3.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:634) ~[tomcat-embed-core-9.0.13.jar:9.0.13]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882) ~[spring-webmvc-5.1.3.RELEASE.jar:5.1.3.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) ~[tomcat-embed-core-9.0.13.jar:9.0.13]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-9.0.13.jar:9.0.13]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.13.jar:9.0.13]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.13.jar:9.0.13]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.13.jar:9.0.13]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.13.jar:9.0.13]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) ~[spring-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.13.jar:9.0.13]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.13.jar:9.0.13]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92) ~[spring-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.13.jar:9.0.13]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.13.jar:9.0.13]
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93) ~[spring-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.13.jar:9.0.13]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.13.jar:9.0.13]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200) ~[spring-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.13.jar:9.0.13]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.13.jar:9.0.13]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199) ~[tomcat-embed-core-9.0.13.jar:9.0.13]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-9.0.13.jar:9.0.13]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490) [tomcat-embed-core-9.0.13.jar:9.0.13]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) [tomcat-embed-core-9.0.13.jar:9.0.13]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [tomcat-embed-core-9.0.13.jar:9.0.13]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [tomcat-embed-core-9.0.13.jar:9.0.13]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) [tomcat-embed-core-9.0.13.jar:9.0.13]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408) [tomcat-embed-core-9.0.13.jar:9.0.13]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-9.0.13.jar:9.0.13]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:791) [tomcat-embed-core-9.0.13.jar:9.0.13]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1417) [tomcat-embed-core-9.0.13.jar:9.0.13]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.13.jar:9.0.13]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_181]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_181]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.13.jar:9.0.13]
at java.lang.Thread.run(Thread.java:748) [na:1.8.0_181]
common-jdbc:一个基于SpringJdbcTemplate的高性能数据库操作工具类库
项目地址:https://gitee.com/cnsugar/common-jdbc
一、简介
基于SpringJdbcTemplate的高性能数据库操作工具类库,支持mysql、oracle数据库,支持类似Hibernate的写法直接对对象进行save或update,支持自动分页。
优点:性能比Mybatis高,不需要写大量的sql,复杂sql组装更灵活,节省开发时间;
缺点:sql嵌在代码中。
使用时注意:需要依赖 https://gitee.com/cnsugar/common 项目,默认工程中lib下放了一个已经打包好的common-1.0.jar,可直接使用;spring配置文件需要以spring-context开头,如果不是需要修改common项目中AppContext类中的源码或新建一个spring-context开头的xml,再导入其他的xml文件。
二、主要类介绍
- DbUtils: 里面包含数据库操作的静态方法,不需要创建Dao类即可对数据库进行操作,查询和删除操作需要完整的sql,查询要直接返回对象时需要指定class,对象可以直接save或update;
- BaseJdbcDao: 里面包含数据库操作的非静态方法,要创建Dao类承继该类使用,可以自动生成查询或删除sql,对单表操作只需要写where后面的语句即可,对象可以直接save或update;
- Table: annotation类,表注解,可单独设置java实体与数据库表的对应关系;
- Field: annotation类,字段映射注解,可单独设置java实体属性与数据库表中字段的对应关系;
- GenerateClass: 开发辅助工具类,自动生成与数据库表对应的实体类代码。(注意:该类只将代码在控制台打印出来了,没有创建文件,需要手动创建class然后把代码拷进去)。
三、使用方式一:继承BaseJdbcDao类
下面通过一个示例来介绍如何使用BaseJdbcDao,例如我们现在要对用户表sys_user进行增、删、查、改操作,分Dao、Service、Model三层结构。
准备:创建一个普通的java项目,引入spring、springjdbc、连接池、数据库驱动的jar包,在spring配置文件中增加jdbcTemplate bean的配置,配置可参考git工程中src/test/resources中的spring-context-db.xml。
1、创建一个空的model类SysUser,然后使用GenerateClass中的方法生成SysUser类的代码。
执行以下代码后,将控制台输出的内容拷到SysUser类中
/**
* 根据表生成实体对象
*/
@org.junit.Test
public void testGenerateClass() {
System.out.println(GenerateClass.getMysqlProperties("SYS_USER"));
}
2、新建一个 SysUserDao 类,继承自 BaseJdbcDao,泛型为SysUser,在类加上@Component注解,并增加三个实现方法deleteById、findById、find,(后面会用到)。
package com.cnsugar.common.jdbctest.dao;
import com.cnsugar.common.jdbc.BaseJdbcDao;
import com.cnsugar.common.jdbctest.bean.SysUser;
import com.cnsugar.common.paging.Page;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
/**
* @Author Sugar
* @Version 2018/12/17 17:24
*/
@Component
public class SysUserDao extends BaseJdbcDao<SysUser> {
/**
* 根据ID删除用户
*
* @param id
* @return
*/
public int deleteById(int id) {
return delete("id=?", new Integer[]{id});
}
/**
* 根据ID查询用户
*
* @param id
* @return
*/
public SysUser findById(int id) {
return queryForObject("id=?", new Integer[]{id});
}
/**
* 分页查询
* @param sysUser
* @param page
* @return
*/
public Page find(SysUser sysUser, Page page) {
StringBuilder wsql = new StringBuilder("1=1");
List<Object> param = new ArrayList<>();
if (StringUtils.isNotEmpty(sysUser.getName())) {//根据姓名查询
wsql.append(" and ").append(SysUser.Column.NAME).append(" like ?");
param.add("%"+sysUser.getName()+"%");
}
if (sysUser.getCreateDate() != null) {//查询创建时间>某个时间的数据
wsql.append(" and ").append(SysUser.Column.CREATE_DATE).append(" > ?");
param.add(sysUser.getCreateDate());
}
return queryForPage(page, wsql.toString(), param.toArray());
}
}
3、创建一个SysUserService,里面实现增、删、查、改几个方法,代码如下:
package com.cnsugar.common.jdbctest.service;
import com.cnsugar.common.jdbctest.bean.SysUser;
import com.cnsugar.common.jdbctest.dao.SysUserDao;
import com.cnsugar.common.paging.Page;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @Author Sugar
* @Version 2018/12/17 18:18
*/
@Service
public class SysUserService {
@Autowired
private SysUserDao sysUserDao;
/**
* 新增用户
*
* @param sysUser
*/
public void save(SysUser sysUser) {
Long id = sysUserDao.save(sysUser);
if (id > 0) {
System.out.println("save ok, id=" + id);
sysUser.setId(id.intValue());
} else {
System.out.println("save fail");
}
}
/**
* 修改用户
*
* @param sysUser
*/
public void update(SysUser sysUser) {
long row = sysUserDao.update(sysUser);
System.out.println("update " + row + " rows.");
}
/**
* 根据ID删除用户
*
* @param id
*/
public void delete(int id) {
int row = sysUserDao.deleteById(id);
System.out.println("delete " + row + " rows.");
}
/**
* 根据ID查询
*
* @param id
* @return
*/
public SysUser findById(int id) {
return sysUserDao.findById(id);
}
/**
* 查询所有数据
*
* @return
*/
public List<SysUser> findAll() {
return sysUserDao.queryForList("1=1");
}
/**
* 分页查询
*
* @param user 查询条件
* @param pageSize 每页显示多少条记录
* @param pageNo 第几页(从1开始)
* @return
*/
public Page findForPage(SysUser user, int pageSize, int pageNo) {
Page page = new Page(pageSize, pageNo);
page.setClazz(SysUser.class);
return sysUserDao.find(user, page);
}
}
4、通过以上三步,就已经完成了对sys_user表的CRUD操作,下面新建单元测试类 Test 测试一下:
package com.cnsugar.common.jdbctest;
import com.cnsugar.common.context.AppContext;
import com.cnsugar.common.jdbctest.bean.SysUser;
import com.cnsugar.common.jdbctest.service.SysUserService;
import com.cnsugar.common.paging.Page;
import java.util.Date;
import java.util.List;
/**
* @Author Sugar
* @Version 2018/12/17 18:47
*/
public class Test {
SysUserService service = AppContext.getBean(SysUserService.class);
/**
* 测试新增
*/
@org.junit.Test
public void testSave() {
SysUser sysUser = new SysUser();
sysUser.setName("张三");
sysUser.setUsername("zhangsan");
sysUser.setPassword("123456");
sysUser.setOfficeId(1);
sysUser.setEmail("cnsugar@qq.com");
sysUser.setPhone("188888888");
sysUser.setCreateDate(new Date());
sysUser.setUpdateDate(new Date());
service.save(sysUser);
System.out.println("新增用户ID="+sysUser.getId());
}
/**
* 测试修改
*/
@org.junit.Test
public void testUpdate() {
SysUser user = service.findById(27);
user.setName("李四");//修改姓名
user.setUpdateDate(new Date());
service.update(user);
}
/**
* 测试查询
*/
@org.junit.Test
public void testQuery() {
List<SysUser> userList = service.findAll();
System.out.println(userList.size());
}
/**
* 测试分页查询
*/
@org.junit.Test
public void testPageQuery() {
//查询7天以内创建的、姓名中带有张的 用户
SysUser user = new SysUser();
user.setName("张");
user.setCreateDate(new Date(System.currentTimeMillis()-7*24*3600*1000));
Page page = service.findForPage(user, 10, 1);
System.out.println("数据列表:"+page.getList());
System.out.println("分页说明html代码:"+page.getMessage());
System.out.println("分页导航html代码:"+page.toString());
}
/**
* 测试删除
*/
@org.junit.Test
public void testDelete() {
service.delete(25);
}
}
四、使用方式二:使用DbUtils工具类
DbUtils中全是静态方法,不需要第一种方式中的第2步来创建一个dao类,直接新建一个测试类Test2来实现上面的功能:
package com.cnsugar.common.jdbctest;
import com.cnsugar.common.jdbc.DbUtils;
import com.cnsugar.common.jdbc.utils.JdbcUtils;
import com.cnsugar.common.jdbctest.bean.SysUser;
import com.cnsugar.common.paging.Page;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* @Author Sugar
* @Version 2018/12/18 11:08
*/
public class Test2 {
/**
* 测试新增
*/
@org.junit.Test
public void testSave() {
SysUser sysUser = new SysUser();
sysUser.setName("张三2");
sysUser.setUsername("zhangsan");
sysUser.setPassword("123456");
sysUser.setOfficeId(1);
sysUser.setEmail("cnsugar@qq.com");
sysUser.setPhone("188888888");
sysUser.setCreateDate(new Date());
sysUser.setUpdateDate(new Date());
long id = DbUtils.save(sysUser);
System.out.println("新增用户ID="+id);
}
/**
* 测试修改
*/
@org.junit.Test
public void testUpdate() {
SysUser user = DbUtils.queryForObject(SysUser.class, "id=?", new Integer[]{28});
user.setName("李四2");//修改姓名
user.setUpdateDate(new Date());
int row = DbUtils.update(user);
System.out.println("update "+row+" rows.");
}
/**
* 测试查询
*/
@org.junit.Test
public void testQuery() {
List<SysUser> userList = DbUtils.queryForList(SysUser.class, "1=1");
System.out.println(userList.size());
}
/**
* 测试分页查询
*/
@org.junit.Test
public void testPageQuery() {
//查询7天以内创建的、姓名中带有张的 用户
SysUser user = new SysUser();
user.setName("张");
user.setCreateDate(new Date(System.currentTimeMillis()-7*24*3600*1000));
StringBuilder wsql = new StringBuilder("1=1");
List<Object> param = new ArrayList<>();
if (StringUtils.isNotEmpty(user.getName())) {//根据姓名查询
wsql.append(" and ").append(SysUser.Column.NAME).append(" like ?");
param.add("%"+user.getName()+"%");
}
if (user.getCreateDate() != null) {//查询创建时间>某个时间的数据
wsql.append(" and ").append(SysUser.Column.CREATE_DATE).append(" > ?");
param.add(user.getCreateDate());
}
int pageSize = 10;
int pageNo = 1;
Page page = new Page(pageSize, pageNo);
page.setClazz(SysUser.class);
page = DbUtils.queryForPage(page, wsql.toString(), param.toArray());
System.out.println("数据列表:"+page.getList());
System.out.println("分页说明html代码:"+page.getMessage());
System.out.println("分页导航html代码:"+page.toString());
}
/**
* 测试删除
*/
@org.junit.Test
public void testDelete() {
//写法一:直接在sql中写出表名
DbUtils.delete("delete sys_user where id=?", new Integer[]{28});
//写法二(推荐):读取实体对应的表名,方便维护(如果改了表名,只需要将实体中@table注解的value改了即可)
DbUtils.delete("delete "+ JdbcUtils.getTableName(SysUser.class)+" where id=?", new Integer[]{28});
}
}
附一个常遇到的问题:我只需要更新某几个字段,但又不想写Sql怎么办?试试下面两个方法^v^
/**
* 根据1个java对象更新数据库对应的表
*
* @param object 对象
* @param columns 需要更新的字段名
* @return
*/
int update(T object, String[] columns);
/**
* 根据1个java对象更新数据库对应的表
*
* @param object 对象
* @param columns 指定要更新或忽略的字段名
* @param ignore true-忽略,false-更新
* @return
*/
int update(T object, String[] columns, boolean ignore);
简单测试一下,只修改姓名这一个字段可以这样写:
SysUser user = new SysUser();
user.setId(28);
user.setName("李四2");//修改姓名
int row = DbUtils.update(user, new String[]{SysUser.Column.NAME});
以上只是对DbUtils和BaseJdbcDao的简单使用介绍,更多方法使用介绍请参考:
http://doc.tinnar.cn/javadoc/common-jdbc
有问题欢迎随时与我交流。email:cnsugar@qq.com
原文出处:https://www.cnblogs.com/cnsugar/p/10133161.html
JDBC + Spring JDBC(JdbcTemplate)
1. JDBC
-- JDBC (Java Database Connectivity) java数据库连接,是java标准库的一部分,主要负责处理对数据库的访问
(纯JDBC代码来访问数据库)
--了解java.sql包中几个对象
a. DriverManager :实用工具类,认识所有可用的数据库驱动程序
b. Connection:代表与数据库的连接,包含url, 用户名,密码等信息,可使用DriverManager来创建他们
c. PreparedStatement 和 CallableStatement:把真实的sql语句发送给数据库服务器。sql语句从Connection创建的
d. esultSet:数据返回方式,可迭代,每个对象代表一行,可使用getFoo(index) / getFoo(name)方法访问数据,其中Foo是数据类型,如String,Int等,index是列号(1开始),name是列名,ResultSet由sql语句返回
e. Date, Time, Timestamp:时间数据的sql表示形式
--java.sql.sqlException: No suitable driver 问题解决
解决办法是将MysqL的驱动jar包mysql-connector-java-8.0.28.jar,放到C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext (针对本机)
@Test
public void testjdbc() throws sqlException {
String url = "jdbc:MysqL://192.168.0.10:3306/agiletc";
Connection conn = DriverManager.getConnection(url,"steve","appleseed");
String sql = "select id,username,password from user";
PreparedStatement ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery();
while(rs.next()){
Integer id = rs.getInt("id");
String name = rs.getString("username");
String password = rs.getString("password");
String msg = String.format("ID: %d, Name: %s, Pwd:%s",id,name,password);
System.out.println(msg);
}
}
2.数据库框架 Spring JDBC--JdbcTemplate
--Spring框架提供很多操作模板类,如操作关系型数据的JdbcTemplate和HibernateTemplate,操作nosql数据库的Redistemplate,操作消息队列的jmstemplate等等
--JdbcTemplate类是最基本的Spring JDBC模板,无需使用纯JDBC也能执行sql语句
--JdbcTemplate受Datasource支持,线程安全,意味着可在整个应用程序中使用同一个实例
--JdbcTemplate流程步骤
a. 导入相关依赖
b. 创建数据库表和实体类
(Entity类:
1.实体类的作用是和数据表做映射,即数据库中的一个表映射成对应的一个java类,其中还有一个映射文件
2.属性对应数据表中的字段, 方法即是对表中数据的操作(setter和getter方法,带参数的构造器和无参数的构造器,重写父类的方法等)
c. 创建JdbcTemplate对象
d. 执行数据库操作
--实操例子
(1)导入相关依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<dependency>
<groupId>MysqL</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.48</version>
</dependency>
(2)MysqL建表
CREATE TABLE account(
`oid` int NOT NULL AUTO_INCREMENT ,
`username` VARCHAR(10) NOT NULL,
`money` int NOT NULL,
PRIMARY KEY(`oid`)
);
(3)创建Entity类
public class Account {
private int oid;
private String username;
private int money;
public int getoid(){
return oid;
}
public void setoid(int oid){
this.oid = oid;
}
public String getUsername(){
return username;
}
public void setUsername(String username){
this.username = username;
}
public int getMoney(){
return money;
}
public void setMoney(int money){
this.money = money;
}
@Override
public String toString(){
return "Account{" + "oid=" + oid + " ,username=" + username + '\'' + ", money=" + money + "}";
}
}
(4)创建JdbcTemplate对象,执行数据库操作
@Test
public void test1() throws PropertyVetoException {
//创建c3p0数据库连接池
ComboPooledDataSource dataSource = new ComboPooledDataSource();
//设置连接参数
dataSource.setDriverClass("com.MysqL.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:MysqL://localhost:3306/testing");
dataSource.setUser("root");
dataSource.setPassword("password");
//创建jdbcTemplate对象
JdbcTemplate jdbcTemplate = new JdbcTemplate();
//给jdbcTemplate对象设置数据源
jdbcTemplate.setDataSource(dataSource);
//插入一条数据
int i = jdbcTemplate.update("insert into account values(?,?,?)",3,"Jhon",1500);
System.out.println(i);
}
--代码优化
(1)jdbc.properties
jdbc.driver=com.MysqL.jdbc.Driver
jdbc.url=jdbc:MysqL://localhost:3306/testing
jdbc.user=root
jdbc.password=password
(2)spring-jdbc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--引入jdbc.properties-->
<context:property-placeholder location="jdbc.properties"/>
<!--配置c3p0连接池-->
<bean id="dataSource">
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.user}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!--配置jdbcTemplate-->
<bean id="jdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
(3)applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="classpath:spring-jdbc.xml"/>
</beans>
(4)测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = "classpath:applicationContext.xml")
public class TestJdbcTemplate {
@Autowired
private JdbcTemplate jdbcTemplate;
//Spring创建jdbcTemplate对象
@Test
public void test2() {
//插入一条数据
int i = jdbcTemplate.update("insert into account values(?,?,?)", 3, "lucy", 5000);
System.out.println(i);
}
}
--JdbcTemplate常用操作(CURD操作)
注意:
a. 如果pojo中没有get/set,那么Springjdbc映射时会找不到属性值,从而为null
b. BeanPropertyRowMapper是RowMapper的实现类
public class BeanPropertyRowMapper<T> implements RowMapper<T>
(1)代码
@Test
public void test3() {
int i = 0;
//插入一条数据
i = jdbcTemplate.update("insert into account values(?,?,?)", 5, "Amy", 5000);
System.out.println("insert:" + i);
//更新操作
i = jdbcTemplate.update("update account set money=? where username =?", 000, "tom");
System.out.println("update:" + i);
//删除操作
i = jdbcTemplate.update("delete from account where oid = ?", 4);
System.out.println("delete:" + i);
//查询全部
List<Account> accounts = jdbcTemplate.query("select * from account",
new BeanPropertyRowMapper<Account>(Account.class));
AtomicInteger j = new AtomicInteger(1);
accounts.forEach((s) -> {
System.out.println("第" + (j.getAndIncrement()) + "条:" + s);
});
//查询单个
Account account = jdbcTemplate.queryForObject("select * from account where username = ?",
new BeanPropertyRowMapper<Account>(Account.class),"lucy");
System.out.println("查询单个" + account);
}
jdbc-plus是一款基于JdbcTemplate增强工具包, 基于JdbcTemplate已实现分页、多租户等插件,可自定义扩展插件
jdbc-plus简介
jdbc-plus是一款基于JdbcTemplate增强工具包, 基于JdbcTemplate已实现分页、多租户等插件,可自定义扩展插件。项目地址: https://github.com/deeround/jdbc-plus
特性:
- 使用简单,对代码入侵很小
- 可自定义任意扩展插件
- 多租户参考mybatis-plus多租户的实现原理,理论上与mybatis-plus多租户插件支持度一样
- 分页参考PageHelper的实现原理,使用简单,对代码入侵较小,还可以注册不支持的数据库
- 免费开源,可任意使用修改代码
快速开始
- 引入jdbc-plus-spring-boot-starter
<dependency>
<groupId>com.github.deeround</groupId>
<artifactId>jdbc-plus-spring-boot-starter</artifactId>
<version>${version}</version>
</dependency>
- 注入需要使用的插件
[@Configuration](https://my.oschina.net/pointdance)
public class JdbcPlusConfig {
/**
* PaginationInterceptor是内置的分页插件(分页插件一定要注入在TenantLineHandler之后,可以通过Order来控制顺序)
*/
[@Bean](https://my.oschina.net/bean)
@Order(9)
public IInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
/**
* TenantLineHandler是内置的多租户插件插件
*/
[@Bean](https://my.oschina.net/bean)
@Order(1)
public IInterceptor tenantLineInterceptor() {
return new TenantLineInterceptor(new TenantLineHandler() {
/**
* 当前租户ID
*/
@Override
public Expression getTenantId() {
String currentTenantId = "test_tenant_1";//可以从请求上下文中获取(cookie、session、header等)
return new StringValue(currentTenantId);
}
/**
* 租户字段名
*/
@Override
public String getTenantIdColumn() {
return "tenant_id";
}
/**
* 根据表名判断是否忽略拼接多租户条件
*/
@Override
public boolean ignoreTable(String tableName) {
return TenantLineHandler.super.ignoreTable(tableName);
}
});
}
}
多租户插件
- 注入多租户插件
/**
* TenantLineHandler是内置的多租户插件插件
*/
@Bean
@Order(1)
public IInterceptor tenantLineInterceptor() {
return new TenantLineInterceptor(new TenantLineHandler() {
/**
* 当前租户ID
*/
@Override
public Expression getTenantId() {
String currentTenantId = "test_tenant_1";//可以从请求上下文中获取(cookie、session、header等)
return new StringValue(currentTenantId);
}
/**
* 租户字段名
*/
@Override
public String getTenantIdColumn() {
return "tenant_id";
}
/**
* 根据表名判断是否忽略拼接多租户条件
*/
@Override
public boolean ignoreTable(String tableName) {
return TenantLineHandler.super.ignoreTable(tableName);
}
});
}
- service层执行SQL时自动添加租户字段
@Autowired
JdbcTemplate jdbcTemplate;
public void insert() {
this.jdbcTemplate.update("insert into test_user(id,name) values(''1'',''wangwu'')");
//最终执行SQL:insert into test_user(id,name,tenant_id) values(''1'',''wangwu'',''test_tenant_1'')
}
public void delete() {
this.jdbcTemplate.update("delete from test_user");
//最终执行SQL:delete from test_user where tenant_id=''test_tenant_1''
}
public void update() {
this.jdbcTemplate.update("update test_user set name=''lisi'' where id=''1''");
//最终执行SQL:update test_user set name=''lisi'' where id=''1'' and tenant_id=''test_tenant_1''
}
public List<Map<String, Object>> query() {
return this.jdbcTemplate.queryForList("select * from test_user");
//最终执行SQL:select * from test_user where tenant_id=''test_tenant_1''
}
分页插件
- 注入分页插件
/**
* PaginationInterceptor是内置的分页插件(分页插件一定要注入在TenantLineHandler之后,可以通过Order来控制顺序)
*/
@Bean
@Order(9)
public IInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
- service层执行SQL时自动对SQL进行分页查询
@Autowired
JdbcTemplate jdbcTemplate;
public PageInfo<Map<String, Object>> page1() {
PageHelper.startPage(1, 2);
List<Map<String, Object>> list = this.jdbcTemplate.queryForList("select * from test_user");//最终执行SQL:select * from test_user LIMIT 0,2
PageInfo<Map<String, Object>> page = new PageInfo<>(list);//PageInfo对象包含了分页信息(总行数等)
return page;
}
public PageInfo<Map<String, Object>> page2() {
PageHelper.startPage(2, 2);
List<Map<String, Object>> list = this.jdbcTemplate.queryForList("select * from test_user");//最终执行SQL:select * from test_user LIMIT 2,2
PageInfo<Map<String, Object>> page = new PageInfo<>(list);//PageInfo对象包含了分页信息(总行数等)
return page;
}
- 自定义分页
当插件不支持的数据库分页,可以通过PageHelper.registerDialectAlias(String alias, Class clazz)
注册一个自己分页实现类即可,也可以覆盖已支持的数据库分页。
自定义插件
示例:写一个打印SQL语句、执行参数、以及执行SQL耗时的监控插件。
- 编写MyStatInterceptor插件
/**
* SQL监控插件
*/
@Slf4j
public class MyStatInterceptor implements IInterceptor {
/**
* 自定义插件是否支持
*/
@Override
public boolean supportMethod(final MethodInvocationInfo methodInfo) {
return IInterceptor.super.supportMethod(methodInfo);
}
/**
* SQL执行前方法(主要用于对SQL进行修改)
*/
@Override
public void beforePrepare(final MethodInvocationInfo methodInfo, JdbcTemplate jdbcTemplate) {
log.info("原始SQL:{}", methodInfo.getSql());
log.info("入参:{}", Arrays.toString(methodInfo.getArgs()));
log.info("执行SQL开始时间:{}", LocalDateTime.now());
methodInfo.getUserAttributes().put("startTime", LocalDateTime.now());
}
/**
* SQL执行完成后方法(主要用于对返回值修改)
*
* @param result 原始返回对象
* @return 处理后的返回对象
*/
@Override
public Object beforeFinish(Object result, final MethodInvocationInfo methodInfo, JdbcTemplate jdbcTemplate) {
log.info("执行SQL结束时间:{}", LocalDateTime.now());
LocalDateTime startTime = (LocalDateTime) methodInfo.getUserAttributes().get("startTime");
log.info("执行SQL耗时:{}毫秒", Duration.between(startTime, LocalDateTime.now()).toMillis());
return result;
}
}
- 注入自定义插件
/**
* 自定义插件注入,注入位置按实际情况
*/
@Bean
@Order(0)
public IInterceptor myStatInterceptor() {
return new MyStatInterceptor();
}
- 查看效果(查看打印日志)
c.g.d.j.p.s.config.MyStatInterceptor : 原始SQL:select * from test_user
c.g.d.j.p.s.config.MyStatInterceptor : 入参:[select * from test_user]
c.g.d.j.p.s.config.MyStatInterceptor : 执行SQL开始时间:2023-04-23T16:35:58.151
c.g.d.j.p.s.config.MyStatInterceptor : 执行SQL结束时间:2023-04-23T16:35:58.655
c.g.d.j.p.s.config.MyStatInterceptor : 执行SQL耗时:503毫秒
★ 鸣谢 ★
https://github.com/baomidou/mybatis-plus
https://github.com/pagehelper/Mybatis-PageHelper
https://github.com/deeround/jdbc-plus
关于使用Mockito模拟JdbcTemplate的数据源和mock jdbc的问题就给大家分享到这里,感谢你花时间阅读本站内容,更多关于20. Spring Boot 默认、自定义数据源 、配置多个数据源 jdbcTemplate操作DB、common-jdbc:一个基于SpringJdbcTemplate的高性能数据库操作工具类库、JDBC + Spring JDBC(JdbcTemplate)、jdbc-plus是一款基于JdbcTemplate增强工具包, 基于JdbcTemplate已实现分页、多租户等插件,可自定义扩展插件等相关知识的信息别忘了在本站进行查找喔。
本文标签: