Spring Data Jpa(三)
阿新 • • 發佈:2018-11-11
Spring Data Jpa介紹
A、Spring Data JPA 是 Spring 基於 ORM 框架、JPA 規範的基礎上封裝的套 JPA 應用框架,
可使開發者用極簡的程式碼即可實現對資料的訪問和操作。它提供了包括增刪改查等在內的常用功能,
且易於擴充套件!學習並使用 Spring Data JPA 可以極大提高開發效率!
B、Spring Data JPA 簡化了 DAO 層的操作,基本上所有 CRUD 都可以依賴於其實現。
1、入門案例
A、新增依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
B、新增配置檔案:
# 配置資料庫
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
# 配置 Jpa 相關引數
spring.jpa.properties.hibernate.hbm2ddl.auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.show-sql= true
# hibernate.hbm2ddl.auto 引數的作用主要用於:自動建立 | 更新 | 驗證資料庫表結構,有四個值:
# create:每次載入 hibernate 時都會刪除上一次的生成的表,然後根據 model 類再重新來生成新表,
# 哪怕兩次沒有任何改變也要這樣執行,這就是導致資料庫表資料丟失的一個重要原因。
# create-drop:每次載入 hibernate 時根據 model 類生成表,但是 sessionFactory 關閉,表就自動刪除。
# update:最常用的屬性,第1次載入 hibernate 時根據 model 類會自動建立起表的結構(前提是先建好
# 資料庫),以後載入 hibernate 時根據 model 類自動更新表結構,即使表結構改變了,但表中的行仍
# 然存在,不會刪除以前的行。要注意的是當部署到伺服器後,表結構是不會被馬上建立起來的,是要等
# 應用第一次執行起來後才會。
# validate:每次載入 hibernate 時,驗證建立資料庫表結構,只會和資料庫中的表進行比較,不會建立新
# 表,但是會插入新值。
# dialect:主要是指定生成表名的儲存引擎為 InneoDB。
# show-sql:是否打印出自動生產的 SQL,方便除錯的時候檢視。
C、新增實體類和Dao : Entity 中不對映成列的欄位得加 @Transient 註解,不加註解也會對映成列
@Entity
public class User implements Serializable {
@Id
@GeneratedValue
private Long id;
@Column(nullable = false,unique = true)
private String userName;
@Column(nullable = false)
private String passWord;
@Column(nullable = false, unique = true)
private String email;
@Column(nullable = false)
private String regTime;
//省略 getter settet 方法、構造方法
}
Dao:
public interface UserRepository extends JpaRepository<User, Long> {
User findByUserName(String userName);
User findByUserNameOrEmail(String username,String email);
}
D、編寫測試類
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserRepositoryTest {
@Resource
private UserRepository userRepository;
@Test
public void findByUserName() throws Exception {
/**
* 資料庫造資料
* Date date=new Date();
* DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG);
* String formatDate = dateFormat.format(date);
* userRepository.save(new User("aa", "aa123456"," [email protected]",formatDate));
* userRepository.save(new User("bb", "bb123456","[email protected]",formatDate));
* userRepository.save(new User("cc", "cc123456","[email protected]",formatDate));
*/
Assert.assertEquals(3,userRepository.findAll().size());
//userRepository.delete(userRepository.findByUserName("aa"));
}
@Test
public void findByUserNameOrEmail() throws Exception {
Assert.assertEquals("bb",userRepository.findByUserNameOrEmail("bb"," [email protected]").getUserName());
}
}
2、基本查詢分為兩種,① Spring Data 預設已經實現 ② 根據查詢的方法來自動解析成 SQL。
A、Spring Data JPA 預設預先生成一些基本的 CURD 的方法,如增、刪、改等。 繼承 JpaRepository: public interface UserRepository extends JpaRepository<User, Long> { } 使用預設方法: @Test public void testBaseQuery() { userRepository.findAll(); userRepository.findOne(1l); userRepository.save(user); userRepository.delete(user); userRepository.count(); userRepository.exists(1l); // ... } B、自定義的簡單查詢就是根據方法名來自動生成 SQL,主要的語法是 findXXBy、readAXXBy、queryXXBy、countXXBy、getXXBy 後面跟屬性名稱: User findByUserName(String userName); 也可加一些關鍵字 And、Or: User findByUserNameOrEmail(String username, String email); 修改、刪除、統計類似語法: Long deleteById(Long id); Long countByUserName(String userName) 基本上 SQL 體系中的關鍵詞都可以使用,如 LIKE、IgnoreCase、OrderBy: List<User> findByEmailLike(String email); User findByUserNameIgnoreCase(String userName); List<User> findByUserNameOrderByEmailDesc(String email);
3、複雜查詢
在實際的開發中需要用到分頁、篩選、連表等查詢的時候就需要特殊的方法或者自定義 SQL。
分頁查詢:
分頁查詢在實際使用中非常普遍,Spring Data JPA 已經幫我們實現了分頁的功能,在查詢的方法中,
需要傳入引數 Pageable,當查詢中有多個引數的時候, Pageable 建議做為最後一個引數傳入
Pageable 是 Spring 封裝的分頁實現類,使用的時候需要傳入頁數、每頁條數和排序規則。
@Query("select u from User u")
Page<User> findALL(Pageable pageable);
Page<User> findByUserName(String userName,Pageable pageable);
測試
@Test
public void testPageQuery(){
int page=1,size=2;
Sort sort=new Sort(Sort.Direction.DESC,"id");
Pageable pageable=new PageRequest(page,size,sort);
userRepository.findALL(pageable);
userRepository.findByUserName("aa",pageable);
}
4、自定義SQL查詢
A、使用 Spring Data 大部分的 SQL 都可以根據方法名定義的方式來實現,如果我們想使用自定義的
SQL 來查詢,Spring Data 也可以支援
B、在 SQL 的查詢方法上面使用 @Query 註解,如涉及到刪除和修改需要加上 @Modifying,也可以
根據需要新增 @Transactional 對事物的支援,查詢超時的設定等。
@Transactional(timeout = 10)
@Modifying
@Query("update User set userName =?1 where id =?2")
int modifyById(String userName,Long id);
@Transactional
@Modifying
@Query("delete from User where id =?1")
void deleteById(Long id);
@Query("select u from User u where u.email = ?1")
User findByEmail(String email);
5、多資料來源的支援(重點)
專案開發中,常需要在一個專案中使用多個數據源,因此需要配置 Spring Data JPA 對多資料來源的使用,一般分為以下三步:
a、配置多資料來源
b、不同源的 repository 放入不同包路徑
c、宣告不同的包路徑下使用不同的資料來源、事務支援
0、實體類所在包 com.kid.domain
@Entity
public class User implements Serializable {
@Id
@GeneratedValue
private Long id;
@Column(nullable = false, unique = true)
private String userName;
@Column(nullable = false)
private String passWord;
@Column(nullable = false, unique = true)
private String email;
@Column(nullable = true, unique = true)
private String nickName;
@Column(nullable = false)
private String regTime;
//getter、setter、構造方法省略...
}
A、配置兩個資料來源:
#primary
spring.primary.datasource.url=jdbc:mysql://localhost:3306/test1
spring.primary.datasource.username=root
spring.primary.datasource.password=root
spring.primary.datasource.driver-class-name=com.mysql.jdbc.Driver
#secondary
spring.secondary.datasource.url=jdbc:mysql://localhost:3306/test2
spring.secondary.datasource.username=root
spring.secondary.datasource.password=root
spring.secondary.datasource.driver-class-name=com.mysql.jdbc.Driver
B、讀取兩個配置源,構建兩個資料來源:
com.kid.config 包下:
@Configuration
public class DataSourceConfig {
@Bean(name = "primaryDataSource")
@Qualifier("primaryDataSource")
@ConfigurationProperties(prefix="spring.primary.datasource")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "secondaryDataSource")
@Qualifier("secondaryDataSource")
@Primary
@ConfigurationProperties(prefix="spring.secondary.datasource")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
}
C、將資料來源注入到 Factory,配置 repository、domian 的位置,需要設定一個預設的資料來源:
com.kid.config 包下:
PrimaryConfig:
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef="entityManagerFactoryPrimary",
transactionManagerRef="transactionManagerPrimary",
basePackages= { "com.kid.repository.test1" })//設定dao(repo)所在位置
public class PrimaryConfig {
@Autowired
private JpaProperties jpaProperties;
@Autowired
@Qualifier("primaryDataSource")
private DataSource primaryDataSource;
@Bean(name = "entityManagerPrimary")
@Primary
public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
return entityManagerFactoryPrimary(builder).getObject().createEntityManager();
}
@Bean(name = "entityManagerFactoryPrimary")
@Primary
public LocalContainerEntityManagerFactoryBean entityManagerFactoryPrimary (EntityManagerFactoryBuilder builder) {
return builder
.dataSource(primaryDataSource)
.packages("com.kid.domain") //設定實體類所在位置
.persistenceUnit("primaryPersistenceUnit")
.properties(getVendorProperties())
.build();
}
private Map<String, Object> getVendorProperties() {
return jpaProperties.getHibernateProperties(new HibernateSettings() );
}
@Bean(name = "transactionManagerPrimary")
@Primary
PlatformTransactionManager transactionManagerPrimary(EntityManagerFactoryBuilder builder) {
return new JpaTransactionManager(entityManagerFactoryPrimary(builder).getObject());
}
}
SecondaryConfig:
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef="entityManagerFactorySecondary",
transactionManagerRef="transactionManagerSecondary",
basePackages= { "com.kid.repository.test2" })
public class SecondaryConfig {
@Autowired
private JpaProperties jpaProperties;
@Autowired
@Qualifier("secondaryDataSource")
private DataSource secondaryDataSource;
@Bean(name = "entityManagerSecondary")
public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
return entityManagerFactorySecondary(builder).getObject().createEntityManager();
}
@Bean(name = "entityManagerFactorySecondary")
public LocalContainerEntityManagerFactoryBean entityManagerFactorySecondary (EntityManagerFactoryBuilder builder) {
return builder
.dataSource(secondaryDataSource)
.properties(getVendorProperties())
.packages("com.kid.domain")
.persistenceUnit("secondaryPersistenceUnit")
.build();
}
private Map<String, Object> getVendorProperties() {
return jpaProperties.getHibernateProperties(new HibernateSettings());
}
@Bean(name = "transactionManagerSecondary")
PlatformTransactionManager transactionManagerSecondary(EntityManagerFactoryBuilder builder) {
return new JpaTransactionManager(entityManagerFactorySecondary(builder).getObject());
}
}
D、配置Dao
com.kid.repository.test1
public interface UserTest1Repository extends JpaRepository<User, Long> {
User findByUserName(String userName);
User findByUserNameOrEmail(String username, String email);
}
com.kid.repository.test2
public interface UserTest2Repository extends JpaRepository<User, Long> {
User findByUserName(String userName);
User findByUserNameOrEmail(String username, String email);
}
E:測試類編寫
@Resource
private UserTest1Repository userTest1Repository;
@Resource
private UserTest2Repository userTest2Repository;
@Test
public void testSave() throws Exception {
Date date = new Date();
DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG);
String formattedDate = dateFormat.format(date);
userTest1Repository.save(new User("aa", "aa123456","[email protected]", "aa", formattedDate));
userTest1Repository.save(new User("bb", "bb123456","[email protected]", "bb", formattedDate));
userTest2Repository.save(new User("cc", "cc123456","[email protected]", "cc", formattedDate));
}
檢視資料庫會發現 test1 會有兩條資料,test2 有一條。在實際使用中需要哪個資料來源使用 @Resource 注入即可。