Spring Boot專案中的資料庫查詢
概述
Spring Boot專案就是尊崇“習慣優於配置“的思想,把過去spring框架專案的各種配置檔案都給了預設配置。這個專案出來好幾年了,相信大部分團隊都用上了。講真,該專案對於擁抱spring專案大腿的java開發者來說真的是太方便了。
我們在spring boot專案中要使用某個技術,也就是需要和spring整合。所以準確的說這篇文章應該講Spring中的資料庫查詢。查詢方式選擇
工作這麼多年,確實各種奇葩的資料庫查詢方式都用過,我認為最爛的就是直接在程式碼中拼接字串式的SQL語句。其中有一些常用的方式,MyBatis,Spring Jpa,Querydsl。。。- 跨庫查詢
按理說在同一個專案跨庫查詢不應該存在。如果出現了,一定是表設計出現問題了。資料庫按應用分,不同應用之間呼叫應該通過RPC。
要實現跨庫查詢其實就是配置多個數據源
列一個配置類UicDatasourceConfig.java,比如我這裡又一個使用者中心(uic)庫:
@Configuration
@PropertySource("classpath:database.properties")
@EnableJpaRepositories(
basePackages = "com.medxi.uic.repository.uic",
entityManagerFactoryRef = "uicEntityManager",
transactionManagerRef = "uicTransactionManager"
)
public class UicDatasourceConfig {
/**
* @return 資料來源
*/
@Bean
@Primary
@ConfigurationProperties(prefix = "datasource.uic")
public DataSource uicDataSource(){
return DataSourceBuilder.create().build();
}
/**
* @return entityManager
*/
@Bean
@Primary
public LocalContainerEntityManagerFactoryBean uicEntityManager (){
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(true);
LocalContainerEntityManagerFactoryBean uicEntityManager = new LocalContainerEntityManagerFactoryBean();
uicEntityManager.setDataSource(uicDataSource());
uicEntityManager.setPackagesToScan("com.medxi.uic.entity.uic");
uicEntityManager.setJpaVendorAdapter(vendorAdapter);
return uicEntityManager;
}
/**
* @return TransactionManager
*/
@Bean
@Primary
public PlatformTransactionManager uicTransactionManager(){
return new JpaTransactionManager(uicEntityManager().getObject());
}
/**
* @return uicEntityManager
*/
@Bean
public EntityManager uicEntityManager(){
return uicEntityManagerFactory().getObject().createEntityManager();
}
}
對應該資料庫源的實體類和repository(dao)層都應該獨立的pakage,在配置的時候要指定。如這裡的:
repository包:com.medxi.uic.repository.uic
entity包:com.medxi.uic.entity.uic
類似配置另一個數據源,注意由於兩個資料來源相關bean都是同類型,所以一個需要註釋@Primary。
不同庫的資料庫資訊配置都可以配置在Resources目錄下的資原始檔database.properties中,根據自己具體配置,選擇字首。
如我這裡uic:
datasource.uic.url=jdbc:mysql://127.0.0.1/um_uic
datasource.uic.username=medxi
.
.
.
由於自己配製了資料來源,就可以不用spring自動配置了,需要在啟動類中排除掉
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
注意使用資料來源注入的時候需要指定,如
@Autowired() @Qualifier("uicEntityManager")
或者
@Resource(name="uicEntityManager")
接下來就可以按照自己需要使用了
4. Spring jpa
這個spring自己家的,使用最方便,基本不需要配置,用spring boot專案的同學應該都瞭解了。
無非就是定義一個基礎BaseRepository,如:
@NoRepositoryBean
public interface BaseRepository<T> extends JpaRepository<T, String>,JpaSpecificationExecutor<T> {
}
繼承的東西可以跟需要進行調整
5. MyBatis
自己寫一堆sql在程式碼中確實很討厭,但是遇到需要跨庫(同資料來源),或者大半頁面都是一個查詢的時候咋辦?所以MyBatis還是很需要的,這裡介紹一下使用MyBatis的配置
首先當然是引入依賴:
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.2.0</version>
</dependency>
有註解的方式和mapper.xml的方式,從上面談到的使用需求來說,在介面上註解一大堆查詢字串(類似spring jpa 的repository上註釋@Query),也是很難看的,所以我就不說了。
使用mapper.xml和普通spring專案比,還是簡化了很多,因為依賴的類中幫我們做了很多事情。
Resources目錄下配置自己的mybatis-config檔案
如mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<mappers>
<mapper resource="mapper/IUICQueryMapper.xml"/>
</mappers>
</configuration>
系統配置檔案中配置指定mybatis-config檔案
mybatis.config-location=classpath:mybatis-config.xml
logging.level.com.medxi.uic.dao=debug #為了列印查詢sql
IUICQueryMapper.xml這個檔案就不多說了,和在spring中使用一樣
com.medxi.uic.dao包下建立IUICQueryMapper.java,類註釋@Mapper,剩下的就和以前使用一樣了
配置卻少了很多。
6. Querydsl
Querydsl的使用,spring boot的官方指南中就提到了。之前看了覺得spring jpa就做了查詢,沒考慮這個。最近遇到大量複雜查詢的時候,又想通過java程式碼優雅體現的時候,終於發現她的好了。
第一步,新增依賴,spring boot已經對其依賴版本做了管理,所以不需要列版本
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
</dependency>
第二步,Maven加入APT外掛
<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>apt-maven-plugin</artifactId>
<version>1.1.3</version>
<executions>
<execution>
<goals>
<goal>process</goal>
</goals>
<configuration>
<outputDirectory>target/generated-sources</outputDirectory>
<processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
</configuration>
</execution>
</executions>
</plugin>
注意每次新增或者修改entity都需要執行maven的編譯,它才會在物件的實體類目錄下建立查詢物件,名字就是實體類名前多個“Q”。這樣才能使用。
第三步,我們建立一個BaseQuerydslRepository.java供其他單個實體查詢時使用
@NoRepositoryBean
public interface BaseQuerydslRepository<T, Q extends EntityPath<?>> extends CrudRepository<T, String>, QueryDslPredicateExecutor<T>, QuerydslBinderCustomizer<Q> {
default void customize(QuerydslBindings bindings, Q root) {
bindings.bind(String.class).first(StringExpression::containsIgnoreCase);
}
}
這樣簡單查詢就可以呼叫方法:
Iterable<T> findAll(Predicate var1);
Predicate由實體類對應的查詢物件生成。
比如現在可以用一個UserRepository去繼承,根據使用者名稱查詢:
QUser user = QUser.user;
userRepository.findAll(user.userName.eq("張三"));
如果比較複雜的多表查詢就使用JPAQuery物件
來個實際例子:
public List<FindPiggeryAmountPm> countCitysPiggery(String provinceName,Long dealerId) {
JPAQuery<?> query = new JPAQuery<Void>(entityManager);
QUnitsprofile unitsprofile = QUnitsprofile.unitsprofile;
QCity city = QCity.city;
QProvince province = QProvince.province;
List<Tuple> tupleList = query.from(unitsprofile)
.select(unitsprofile.cityId.count(),
city.cityName,
unitsprofile.latitude,
unitsprofile.longitude)
.leftJoin(unitsprofile.stateId,province)
.leftJoin(unitsprofile.cityId,city)
.where(unitsprofile.unitType.id.eq(Constant.UNIT_TYPE_PIGGERY),
province.provinceName.eq(provinceName),
unitsprofile.id.notIn(getConcernUnitIds(dealerId))
).groupBy(unitsprofile.cityId).fetch();
return tupleList.stream().map(FindPiggeryAmountPm::analyticCitysPiggery).collect(Collectors.toList());
}
像SQL一樣寫java程式碼,看起就舒服多了。
注意:每次查詢都應該是獨立的查詢物件JPAQuery,所以不要配置成Spring Bean。
entityManager直接注入即可:
@PersistenceContext
private EntityManager entityManager;