1. 程式人生 > >Spring Boot專案中的資料庫查詢

Spring Boot專案中的資料庫查詢

  1. 概述
    Spring Boot專案就是尊崇“習慣優於配置“的思想,把過去spring框架專案的各種配置檔案都給了預設配置。這個專案出來好幾年了,相信大部分團隊都用上了。講真,該專案對於擁抱spring專案大腿的java開發者來說真的是太方便了。
    我們在spring boot專案中要使用某個技術,也就是需要和spring整合。所以準確的說這篇文章應該講Spring中的資料庫查詢。

  2. 查詢方式選擇
    工作這麼多年,確實各種奇葩的資料庫查詢方式都用過,我認為最爛的就是直接在程式碼中拼接字串式的SQL語句。其中有一些常用的方式,MyBatis,Spring Jpa,Querydsl。。。

  3. 跨庫查詢
    按理說在同一個專案跨庫查詢不應該存在。如果出現了,一定是表設計出現問題了。資料庫按應用分,不同應用之間呼叫應該通過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;