序文
この記事では主に、Mybatis、Druid、PageHelperのSpringBoot統合、および複数のデータソースとページングの実現について説明します。その中で、SpringBootは前回の記事で説明したMybatisを統合しているので、ここではあまり説明しません。焦点は、複数のデータソースでDruidとPageHelperを構成して使用する方法にあります。
SpringBoot + Mybatis + Druid + PageHelperの調査ノートと最新のインタビューの質問
ドルイドの紹介と使用
ドルイドを使用する前に、ドルイドについて簡単に理解しましょう。
Druidはデータベース接続プールです。ドルイドは、現時点で最高のデータベース接続プールであると言えます。その優れた機能、パフォーマンス、スケーラビリティにより、開発者に支持されています。
DruidはAlibabaに600を超えるアプリケーションを展開し、1年以上にわたって大規模な本番環境で厳密にテストされています。Druidは、Alibabaによって開発されたMonitoringと呼ばれるデータベース接続プールです。
同時に、Druidはデータベース接続プールであるだけでなく、Druidコアには主に次の3つの部分が含まれています。
-
フィルターチェーンモデルに基づくプラグインシステム。
-
DruidDataSource効率的で管理しやすいデータベース接続プール。
-
SQLParser
ドルイドの主な機能は次のとおりです。
-
これは、効率的で強力かつスケーラブルなデータベース接続プールです。
-
データベースアクセスのパフォーマンスを監視できます。
-
データベースパスワードの暗号化
-
SQL実行ログを取得する
-
JDBCを拡張する
紹介については詳しく説明しません。詳細については、公式ドキュメントをご覧ください。それでは、ドルイドの使い方を紹介しましょう。
1つ目はMavenの依存関係であり、ドルイドjarを追加するだけで済みます。
<dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.8</version> </dependency>
構成に関しては、application.propertiesまたはapplication.ymlに以下を追加するだけで済みます。
注:ここでは2つのデータソースを使用しているため、わずかに異なります。Druid構成の説明は以下で非常に詳細に説明されているため、ここでは説明しません。
# 默认的数据源master.datasource.url=jdbc:mysql://localhost:3306/springBoot?useUnicode=true&characterEncoding=utf8&allowMultiQueries=truemaster.datasource.username=rootmaster.datasource.password=123456master.datasource.driverClassName=com.mysql.jdbc.Driver
# 另一个的数据源cluster.datasource.url=jdbc:mysql://localhost:3306/springBoot_test?useUnicode=true&characterEncoding=utf8cluster.datasource.username=rootcluster.datasource.password=123456cluster.datasource.driverClassName=com.mysql.jdbc.Driver
# 连接池的配置信息# 初始化大小,最小,最大spring.datasource.type=com.alibaba.druid.pool.DruidDataSourcespring.datasource.initialSize=5spring.datasource.minIdle=5spring.datasource.maxActive=20
# 配置获取连接等待超时的时间spring.datasource.maxWait=60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒spring.datasource.timeBetweenEvictionRunsMillis=60000
# 配置一个连接在池中最小生存的时间,单位是毫秒spring.datasource.minEvictableIdleTimeMillis=300000spring.datasource.validationQuery=SELECT 1 FROM DUALspring.datasource.testWhileIdle=truespring.datasource.testOnBorrow=falsespring.datasource.testOnReturn=false# 打开PSCache,并且指定每个连接上PSCache的大小spring.datasource.poolPreparedStatements=truespring.datasource.maxPoolPreparedStatementPerConnectionSize=20
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙spring.datasource.filters=stat,wall,log4j# 通过connectProperties属性来打开mergeSql功能;慢SQL记录spring.datasource.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
構成ファイルを正常に追加したら、Druid関連のクラスを作成しましょう。
1つ目は、デフォルトのデータソース構成クラスであるMasterDataSourceConfig.javaクラスです。
@Configuration
@MapperScan(basePackages = MasterDataSourceConfig.PACKAGE, sqlSessionFactoryRef = "masterSqlSessionFactory")public class MasterDataSourceConfig { static final String PACKAGE = "com.pancm.dao.master"; static final String MAPPER_LOCATION = "classpath:mapper/master/*.xml";
@Value("${master.datasource.url}") private String url;
@Value("${master.datasource.username}") private String username;
@Value("${master.datasource.password}") private String password;
@Value("${master.datasource.driverClassName}") private String driverClassName;
@Value("${spring.datasource.initialSize}") private int initialSize;
@Value("${spring.datasource.minIdle}") private int minIdle;
@Value("${spring.datasource.maxActive}") private int maxActive;
@Value("${spring.datasource.maxWait}") private int maxWait;
@Value("${spring.datasource.timeBetweenEvictionRunsMillis}") private int timeBetweenEvictionRunsMillis;
@Value("${spring.datasource.minEvictableIdleTimeMillis}") private int minEvictableIdleTimeMillis;
@Value("${spring.datasource.validationQuery}") private String validationQuery;
@Value("${spring.datasource.testWhileIdle}") private boolean testWhileIdle;
@Value("${spring.datasource.testOnBorrow}") private boolean testOnBorrow;
@Value("${spring.datasource.testOnReturn}") private boolean testOnReturn;
@Value("${spring.datasource.poolPreparedStatements}") private boolean poolPreparedStatements;
@Value("${spring.datasource.maxPoolPreparedStatementPerConnectionSize}") private int maxPoolPreparedStatementPerConnectionSize;
@Value("${spring.datasource.filters}") private String filters;
@Value("{spring.datasource.connectionProperties}") private String connectionProperties;
@Bean(name = "masterDataSource")
@Primary
public DataSource masterDataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
dataSource.setDriverClassName(driverClassName); //具体配置
dataSource.setInitialSize(initialSize);
dataSource.setMinIdle(minIdle);
dataSource.setMaxActive(maxActive);
dataSource.setMaxWait(maxWait);
dataSource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
dataSource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
dataSource.setValidationQuery(validationQuery);
dataSource.setTestWhileIdle(testWhileIdle);
dataSource.setTestOnBorrow(testOnBorrow);
dataSource.setTestOnReturn(testOnReturn);
dataSource.setPoolPreparedStatements(poolPreparedStatements);
dataSource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
try { dataSource.setFilters(filters);
} catch (SQLException e) {
e.printStackTrace();
} dataSource.setConnectionProperties(connectionProperties);
return dataSource; }
@Bean(name = "masterTransactionManager")
@Primary public DataSourceTransactionManager masterTransactionManager() {
return new DataSourceTransactionManager(masterDataSource()); }
@Bean(name = "masterSqlSessionFactory")
@Primary public SqlSessionFactory masterSqlSessionFactory(@Qualifier("masterDataSource") DataSource masterDataSource) throws Exception {
final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); sessionFactory.setDataSource(masterDataSource);
sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources(MasterDataSourceConfig.MAPPER_LOCATION)); return sessionFactory.getObject(); }}
これらの2つの注釈は次のことを説明しています。
-
@Primary:同じタイプの複数のBeanの候補である場合、このBeanが最初に考慮されることをマークします。複数のデータソースを構成する場合は、プライマリデータソースが必要であることに注意し、@ Primaryを使用してBeanをマークします。
-
@MapperScan:Mapperインターフェースとコンテナー管理をスキャンします。
sqlSessionFactoryRefは、一意のSqlSessionFactoryインスタンスを定義することを意味することに注意してください。
上記の構成が完了したら、Druidを接続プールとして使用できます。ただし、Druidは単なる接続プールではなく、監視アプリケーションとも言えます。Web監視インターフェイスが付属しており、SQL関連の情報を明確に確認できます。
SpringBootでDruidの監視機能を使用するには、StatViewServletクラスとWebStatFilterクラスを記述して、登録サービスとフィルタリングルールを実装するだけです。ここでは、@ Configurationと@Beanを使用して、これら2つを一緒に記述できます。
理解を容易にするために、関連する構成手順もコードに記述されているため、ここでは繰り返しません。
コードは次のように表示されます。
@Configurationpublic class DruidConfiguration {
@Bean public ServletRegistrationBean druidStatViewServle() {
//注册服务 ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(
new StatViewServlet(), "/druid/*");
// 白名单(为空表示,所有的都可以访问,多个IP的时候用逗号隔开) servletRegistrationBean.addInitParameter("allow", "127.0.0.1");
// IP黑名单 (存在共同时,deny优先于allow) servletRegistrationBean.addInitParameter("deny", "127.0.0.2");
// 设置登录的用户名和密码 servletRegistrationBean.addInitParameter("loginUsername", "pancm"); servletRegistrationBean.addInitParameter("loginPassword", "123456");
// 是否能够重置数据. servletRegistrationBean.addInitParameter("resetEnable", "false");
return servletRegistrationBean; }
@Bean public FilterRegistrationBean druidStatFilter() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean( new WebStatFilter());
// 添加过滤规则 filterRegistrationBean.addUrlPatterns("/*");
// 添加不需要忽略的格式信息 filterRegistrationBean.addInitParameter("exclusions",
"*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"); System.out.println("druid初始化成功!"); return filterRegistrationBean; }}
書き込み後、プログラムを起動し、ブラウザにhttp://127.0.0.1:8084/druid/index.htmlと入力してから、Webインターフェイスにアクセスするために設定されたユーザー名とパスワードを入力します。
複数のデータソース構成
複数のデータソースを設定する前に、springBootおよびspringBoot_testのmysqlデータベースでそれぞれ次のスクリプトを実行してください。
-- springBoot库的脚本CREATE TABLE `t_user`
( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增id', `name` varchar(10) DEFAULT NULL COMMENT '姓名', `age` in
t(2) DEFAULT NULL COMMENT '年龄', PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT
CHARSET=utf8-- springBoot_test库的脚本CREATE TABLE `t_student` ( `id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(16) DEFAULT NULL, `age` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8
注:怠惰になるために、2つのテーブルの構造は同じになっています!しかし、それはテストには影響しません!
これら2つのデータソースの情報はapplication.propertiesで構成されており、構成は1回投稿されているため、ここでは投稿しません。
ここでは、2番目のデータソースの構成に焦点を当てます。上記のMasterDataSourceConfig.javaと同様に、違いは@Primaryアノテーションと名前が使用されないことです。ここでの2番目のデータソースと同様に、MasterDataSourceConfig.javaはパッケージとマッパーをディレクトリに正確にスキャンすることに注意してください。
その場合、コードは次のようになります。
@Configuration@MapperScan(basePackages
= ClusterDataSourceConfig.PACKAGE, sqlSessionFactoryRef
= "clusterSqlSessionFactory")public class ClusterDataSourceConfig { static final String PACKAGE
= "com.pancm.dao.cluster"; static final String MAPPER_LOCATION = "classpath:mapper/cluster/*.xml";
@Value("${cluster.datasource.url}") private String url;
@Value("${cluster.datasource.username}") private String username;
@Value("${cluster.datasource.password}") private String password;
@Value("${cluster.datasource.driverClassName}") private String driverClass;
// 和MasterDataSourceConfig一样,这里略 @Bean(name
= "clusterDataSource") public DataSource clusterDataSource() { DruidDataSource dataSource
= new DruidDataSource();
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
dataSource.setDriverClassName(driverClass); /
/ 和MasterDataSourceConfig一样,这里略 ...
return dataSource; } @Bean(name
= "clusterTransactionManager") public DataSourceTransactionManager clusterTransactionManager() {
return new DataSourceTransactionManager(clusterDataSource()); } @Bean(name
= "clusterSqlSessionFactory") public SqlSessionFactory clusterSqlSessionFactory(@Qualifier("clusterDataS
ource") DataSource clusterDataSource) throws Exception { final SqlSessionFactoryBean
sessionFactory
= new SqlSessionFactoryBean(); sessionFactory.setDataSource(clusterDataSource); sessionFactory.s
etMapperLocations(new PathMatchingResourcePatternResolver().getResources(ClusterDataSourceConfig.
MAPPER_LOCATION)); return sessionFactory.getObject(); }}
構成を正常に書き込んだ後、プログラムを起動してテストします。
springBootおよびspringBoot_testライブラリのインターフェースを使用してデータを追加します。
t_user
POST http://localhost:8084/api/user{"name":"张三","age":25}{"name":"李四","age":25}{"name":"王五","age":25}
t_student
POST http://localhost:8084/api/student{"name":"学生A","age":16}{"name":"学生B","age":17}{"name":"学生C","age":18}
データが正常に追加されたら、さまざまなインターフェイスを呼び出してクエリを実行します。
リクエスト:
GET http:// localhost:8084 / api / user?name =李四
戻り値:
{ "id": 2, "name": "李四", "age": 25}
リクエスト:
GET http:// localhost:8084 / api / student?name =学生C
戻り値:
{ "id": 1, "name": "学生C", "age": 16}
データから、複数のデータソースが正常に構成されていることがわかります。
PageHelperページネーションの実装
PageHelperはMybatisのページングプラグインで、非常に使いやすいです!ここで強くお勧めします!!!
PageHelperの使用は非常に簡単で、Mavenにpagehelperの依存関係を追加するだけです。
Mavenの依存関係は次のとおりです。
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.3</version></dependency>
注:ここではspringBootバージョンを使用しています!他のバージョンも使用できます。
依存関係を追加した後は、次の構成またはコードを追加するだけで済みます。
1つ目は、application.propertiesまたはapplication.ymlを追加することです。
pagehelper: helperDialect: mysql offsetAsPageNum: true rowBoundsWithCount: true reasonable: false
2つ目は、mybatis.xml構成を追加することです。
<bean
id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSourc
e" ref="dataSource" /> <!-- 扫描mapping.xml文件 --
> <property name="mapperLocations" value="classpath:mapper/*.xml"></property> <!-- 配置分页插件 --
> <property name="plugins"> <array> <bean class="com.github.pagehelper.PageHelper">
<property name="properties"> <value> helperDialect=mysql offsetAsPageNum=t
rue rowBoundsWithCount=true reasonable=false </value> </property>
</bean> </array> </property> </bean>
3つ目は、コードを追加し、@ Beanアノテーションを使用してプログラムの起動時に初期化することです。
@Bean public PageHelper pageHelper(){ PageHelper pageHelper = new PageHelper(); Properties
properties = new Properties(); //数据库 properties.setProperty("helperDialect", "mysql"); //是否将参数
offset作为PageNum使用 properties.setProperty("offsetAsPageNum", "true");
//是否进行count查
询 properties.setProperty("rowBoundsWithCount", "true");
//是否分页合理
化 properties.setProperty("reasonable", "false"); pageHelper.setProperties(properties); }
ここでは複数のデータソースを使用しているため、ここでの構成は少し異なります。ここでsessionFactoryで構成する必要があります。これは、MasterDataSourceConfig.javaに対応する変更です。
masterSqlSessionFactoryメソッドに、次のコードを追加します。
@Bean(name = "masterSqlSessionFactory")
@Primary public SqlSessionFactory masterSqlSessionFactory(
@Qualifier("masterDataSource") DataSource masterDataSource)
throws Exception { final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(masterDataSource);
sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources(MasterDataSourceConfig.MAPPER_LOCATION));
//分页插件 Interceptor interceptor = new PageInterceptor(); Properties properties = new Properties();
//数据库 properties.setProperty("helperDialect", "mysql");
//是否将参数offset作为PageNum使用 properties.setProperty("offsetAsPageNum", "true");
//是否进行count查询 properties.setProperty("rowBoundsWithCount", "true");
//是否分页合理化
properties.setProperty("reasonable", "false"); interceptor.setProperties(properties); sessionFactory.
setPlugins(new Interceptor[] {interceptor}); return sessionFactory.getObject(); }
注:他のデータソースもページングが必要な場合は、上記のコードを参照してください。
ここで注意する必要があるのは、妥当なパラメーターです。これは、ページングが合理化され、デフォルト値がfalseであることを意味します。このパラメーターがtrueに設定されている場合、最初のページはpageNum <= 0のときに照会され、最後のページはpageNum> pagesのときに照会されます(総数を超えた場合)。デフォルトがfalseの場合、パラメーターに基づいて直接クエリを実行します。
PageHelperを設定した後、それを使用する場合は、クエリsqlの前にPageHelper.startPage(pageNum、pageSize);を追加するだけで済みます。総数を知りたい場合は、それを購入してpage.getTotal()を追加できます。クエリSQLステートメントの後。
コード例:
public List<T> findByListEntity(T entity) { List<T> list = null;
try { Page<?> page =PageHelper.startPage(1,2);
System.out.println(getClassName(entity)+"设置第一页两条数据!");
list = getMapper().findByListEntity(entity);
System.out.println("总共有:"+page.getTotal()+"条数据,实际返回:"+list.size()+"两条数据!"); } catch (Exception e) {
logger.error("查询"+getClassName(entity)+"失败!原因是:",e); }
return list; }
コードが書かれた後、最終テストが始まります。
t_userテーブル内のすべてのデータをクエリし、ページングを実行します。
リクエスト:
GET http:// localhost:8084 / api / user
戻り値:
[ { "id": 1, "name": "张三", "age": 25 },
{ "id": 2, "name": "李四", "age": 25 }]
コンソール印刷:
开始查询...User设置第一页两条数据!2018-04-27 19:55:50.769 DEBUG 6152 --- [io-8084-exec-
10] c.p.d.m.UserDao.findByListEntity_COUNT :
==> Preparing: SELECT count(0) FROM t_user
WHERE 1 = 12018-04-27 19:55:50.770 DEBUG 6152 --- [io-8084-exec-
10] c.p.d.m.UserDao.findByListEntity_COUNT :
==> Parameters:2018-04-27 19:55:50.771 DEBUG 6152 ---
[io-8084-exec-10] c.p.d.m.UserDao.findByListEntity_COUNT :
<== Total: 12018-04-
27 19:55:50.772 DEBUG 6152 --- [io-8084-exec-10] c.p.dao.master.UserDao.findByListEntity :
==>
Preparing: select id, name, age from t_user where 1=1 LIMIT ?2018-04-27 19:55:50.773 DEBUG 6152 ---
[io-8084-exec-10] c.p.dao.master.UserDao.findByListEntity :
==> Parameters: 2(Integer)2018-04-
27 19:55:50.774 DEBUG 6152 --- [io-8084-exec-10] c.p.dao.master.UserDao.findByListEntity :
<== Total: 2总共有:3条数据,实际返回:2两条数据!
t_studentテーブルのすべてのデータをクエリし、ページングを実行します。
リクエスト:
GET http:// localhost:8084 / api / student
戻り値:
[
{ "id": 1, "name": "学生A", "age": 16 },
{ "id": 2, "name": "学生B", "age": 17 }]
コンソール印刷:
开始查询...Studnet设置第一页两条数据!2018-04-27 19:54:56.155 DEBUG 6152 ---
[nio-8084-exec-8] c.p.d.c.S.findByListEntity_COUNT :
==> Preparing: SELECT count(0) FROM t_student WHERE 1 = 12018-04-27 19:54:56.155 DEBUG 6152 --- [nio-8084-exec-8] c.p.d.c.S.findByListEntity_COUNT :
==> Parameters:2018-04-27 19:54:56.156 DEBUG 6152 --- [nio-8084-exec-8] c.p.d.c.S.findByListEntity_COUNT :
<== Total: 12018-04-27 19:54:56.157 DEBUG 6152 --- [nio-8084-exec-8] c.p.d.c.StudentDao.findByListEntity :
==> Preparing: select id, name, age from t_student where 1=1 LIMIT ?2018-04-27 19:54:56.157 DEBUG 6152 --- [nio-8084-exec-8] c.p.d.c.StudentDao.findByListEntity :
=
=> Parameters: 2(Integer)2018-04-27 19:54:56.157 DEBUG 6152 --- [nio-8084-exec-8] c.p.d.c.StudentDao.findByListEntity :
<== Total: 2总共有:3条数据,实际返回:2两条数据!
クエリが完了したら、Druidの監視インターフェイスを見てみましょう。
ブラウザに入力します:http://127.0.0.1:8084 / druid / index.html
運転記録がはっきりとわかります!
ドルイドについてもっと知りたい場合は、公式文書をチェックしてください!
結論
この記事はようやく完成しました。コードを書いているときに、たくさんの問題に遭遇し、それを解決するための情報をゆっくりと見つけました。この記事では、これらの関連する使用法を簡単に紹介するだけであり、実際のアプリケーションはもっと複雑になる可能性があります。より良いアイデアや提案があれば、議論のためにメッセージを残してください!
参考記事:https://www.bysocket.com/?p = 1712
Duridの公式アドレス:https://github.com/alibaba/druid
PageHelperの公式アドレス:https://github.com/pagehelper/Mybatis-PageHelper