一窿克、SpringData簡介
Spring Data是一個用于簡化數(shù)據(jù)庫訪問,并支持云服務(wù)的開源框架戚啥。其主要目標(biāo)是使得對數(shù)據(jù)的訪問變得方便快捷。
- Spring Data JPA能干什么
可以極大的簡化JPA的寫法锉试,可以在幾乎不用寫實現(xiàn)的情況下猫十,實現(xiàn)對數(shù)據(jù)的訪問和操作。除了CRUD外呆盖,還包括如分頁拖云、排序等一些常用的功能。 - Spring Data JPA 有什么
主要來看看Spring Data JPA提供的接口应又,也是Spring Data JPA的核心概念:
1:Repository:最頂層的接口宙项,是一個空的接口,目的是為了統(tǒng)一所有Repository的類型株扛,且能讓組件掃描的時候自動識別尤筐。
2:CrudRepository :是Repository的子接口汇荐,提供CRUD的功能
3:PagingAndSortingRepository:是CrudRepository的子接口,添加分頁和排序的功能
4:JpaRepository:是PagingAndSortingRepository的子接口叔磷,增加了一些實用的功能拢驾,比如:批量操作等。
5:JpaSpecificationExecutor:用來做負(fù)責(zé)查詢的接口
6:Specification:是Spring Data JPA提供的一個查詢規(guī)范改基,要做復(fù)雜的查詢繁疤,只需圍繞這個規(guī)范來設(shè)置查詢條件即可 - 特征
1)強大的存儲庫和自定義對象映射抽象
2)從存儲庫方法名稱中進(jìn)行動態(tài)查詢導(dǎo)出
3)實現(xiàn)域基類提供基本屬性
4)支持透明審核(創(chuàng)建,最后更改)
5)集成自定義存儲庫代碼的可能性
6)Easy Spring通過JavaConfig和自定義XML命名空間進(jìn)行集成
7)與Spring MVC控制器進(jìn)行高級集成
二秕狰、Maven依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
三稠腊、application.properties配置
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/iotManager?useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&failOverReadOnly=false&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=1234
#自動建表
spring.jpa.hibernate.ddl-auto=update
#設(shè)置數(shù)據(jù)庫方言
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
#打印sql
spring.jpa.show-sql=true
Jpa的配置后 jpa.hibernate.ddl-auto= update,在其他低版本的SpringBoot中也有使用spring.jpa.properties.hibernate.hbm2ddl.auto=create-drop 這種配置的,具體根據(jù)版本而定鸣哀。該配置的主要作用是:自動創(chuàng)建架忌、更新、驗證數(shù)據(jù)庫結(jié)構(gòu)
1我衬、create:每次加載hibernate時都會刪除上一次的生成的表叹放,然后根據(jù)你的model類再重新來生成新表,哪怕兩次沒有任何改變也要這樣執(zhí)行挠羔,這就是導(dǎo)致數(shù)據(jù)庫表數(shù)據(jù)丟失的一個重要原因(一般只會在第一次創(chuàng)建時使用)
2井仰、create-drop:每次加載hibernate時根據(jù)model類生成表,但是sessionFactory一關(guān)閉,表就自動刪除
3破加、update:最常用的屬性俱恶,第一次加載hibernate時根據(jù)model類會自動建立起表的結(jié)構(gòu)(前提是先建立好數(shù)據(jù)庫),以后加載hibernate時根據(jù)model類自動更新表結(jié)構(gòu)范舀,即使表結(jié)構(gòu)改變了但表中的行仍然存在不會刪除以前的行合是。要注意的是當(dāng)部署到服務(wù)器后,表結(jié)構(gòu)是不會被馬上建立起來的锭环,是要等應(yīng)用第一次運行起來后才會
4聪全、validate:每次加載hibernate時,驗證創(chuàng)建數(shù)據(jù)庫表結(jié)構(gòu)田藐,只會和數(shù)據(jù)庫中的表進(jìn)行比較荔烧,不會創(chuàng)建新表,但是會插入新值汽久。
四鹤竭、創(chuàng)建實體
創(chuàng)建一個User類,配置好上面的信息后景醇,啟動項目臀稚,對應(yīng)的數(shù)據(jù)庫就會自動生成對應(yīng)的表結(jié)構(gòu)。@Table三痰、@Entity吧寺、@Id等注解是jpa的相關(guān)知識窜管。
@Table(name = "t_user")
@Entity
@Data
public class User implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 設(shè)為主鍵 唯一不能為空
* nullable 是否可以為空
* unique 唯一
* @GeneratedValue
* 就是為一個實體生成一個唯一標(biāo)識的主鍵
* (JPA要求每一個實體Entity,必須有且只有一個主鍵)
* @GeneratedValue提供了主鍵的生成策略。
* 稚机。@GeneratedValue注解有兩個屬性,分別是strategy和generator,
* 其中g(shù)enerator屬性的值是一個字符串,默認(rèn)為"",其聲明了主鍵生成器的名稱
* (對應(yīng)于同名的主鍵生成器@SequenceGenerator和@TableGenerator)幕帆。
* JPA為開發(fā)人員提供了四種主鍵生成策略,其被定義在枚舉類GenerationType中,
* 包括GenerationType.TABLE,GenerationType.SEQUENCE,
* GenerationType.IDENTITY和GenerationType.AUTO。
* 這里生成策略為自增長
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
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;
五赖条、創(chuàng)建Dao
方法的名稱要遵循 findBy + 屬性名(首字母大寫) + 查詢條件(首字母大寫 Is Equals)
findByNameLike(String name)
findByName(String name)
findByNameAndAge(String name, Integer age)
findByNameOrAddress(String name) 等...
@Repository
public interface UserRepository extends JpaRepository<User,Long> {
/**
* 根據(jù)年紀(jì)查詢用戶
* @param age
* @return
*/
User findByAge(Integer age);
/**
* 根據(jù)年紀(jì)和姓名查詢
* @param name
* @param age
* @return
*/
User findByNameAndAge(String name, Integer age);
/**
* 對于復(fù)雜查詢可以使用@Query 編寫sql
* @param name
* @return
*/
@Query("from User u where u.name=:name")
User findUser(@Param("name") String name);
}
該Dao成繼承了JpaRepository接口失乾,指定了需要操作的實體對象和實體對象的主鍵類型,通過查看JpaRepository接口源碼可以看到纬乍,里面已經(jīng)封裝了創(chuàng)建(save)碱茁、更新(save)、刪除(delete)仿贬、查詢(findAll纽竣、findOne)等基本操作的函數(shù),使用起來非常方便了茧泪,但是還是會存在一些復(fù)雜的sql蜓氨,spring-data-jpa還提供了一個非常方便的方式,通過實體屬性來命名方法队伟,它會根據(jù)命名來創(chuàng)建sql查詢相關(guān)數(shù)據(jù)语盈,對應(yīng)更加復(fù)雜的語句,還可以用直接寫sql來完成缰泡。
繼承了JpaRepository就相當(dāng)于有了下面的數(shù)據(jù)訪問操作方法:
@NoRepositoryBean
public interface JpaRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID> {
List<T> findAll();
List<T> findAll(Sort var1);
List<T> findAll(Iterable<ID> var1);
<S extends T> List<S> save(Iterable<S> var1);
void flush();
<S extends T> S saveAndFlush(S var1);
void deleteInBatch(Iterable<T> var1);
void deleteAllInBatch();
T getOne(ID var1);
}
六、單元測試
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringdataApplicationTests {
@Autowired
private UserRepository userRepository;
/**
* 新增用戶
* @throws Exception
*/
@Test
public void testAddUser() throws Exception {
User user = new User();
user.setName("zhangsan");
user.setAge(12);
userRepository.save(user);
User user2 = new User();
user2.setName("lishi");
user2.setAge(22);
userRepository.save(user2);
}
/**
* 刪除用戶(根據(jù)對象刪除時代嗤,必須要有ID屬性)
* @throws Exception
*/
@Test
public void testDelUser() throws Exception {
User user = new User();
user.setId(1L);
user.setName("zhangsan");
user.setAge(12);
userRepository.delete(user);
}
/**
* 修改用戶信息
* @throws Exception
*/
@Test
public void testUpdUser() throws Exception {
User user = new User();
user.setId(2L);
user.setName("zhangsan11");
user.setAge(122);
userRepository.save(user);
}
/**
* 查詢用戶
* @throws Exception
*/
@Test
public void testQueryUser() throws Exception {
User user = userRepository.findByAge(22);
System.out.println(user.getName());
User user2 = userRepository.findByNameAndAge("lishi", 22);
System.out.println(user2.getName());
User user3 = userRepository.findUser("zhangsan11");
System.out.println(user3.getName());
}
/**
* 查詢所有用戶
* @throws Exception
*/
@Test
public void testQueryUserList() throws Exception {
List<User> list = userRepository.findAll();
for (User user : list) {
System.out.println(user.getName());
}
}
}
如果沒有表棘钞,將會自動生成。
七干毅、主要接口
CrudReposiroty : 繼承了Repository
Crud主要是添加了對數(shù)據(jù)的增刪改查的方法
PagingAndSortingRepository: 繼承了CrudRepository
JPARepository: 繼承了PagingAndSortingRepository接口
JpaSpecificationExecutor: 這個接口單獨存在宜猜,沒有繼承以上說的接口
主要提供了多條件查詢的支持,并且可以在查詢中添加分頁和排序硝逢。
因為這個接口單獨存在姨拥,因此需要配合以上說的接口使用,如:
/**
* JpaSpecificationExecutor是單獨存在的渠鸽,需要配合這JpaRepository一起使用
*/
@Repository
public interface UserJpaSpecificationExecutor extends JpaSpecificationExecutor<User>, JpaRepository<User, Integer> {
}
八叫乌、JPA的查詢方法
基于@Query注解的查詢和更新:
/**
* SQL nativeQuery的值是true 執(zhí)行的時候不用再轉(zhuǎn)化
* @param name
* @return
*/
@Query(value = "SELECT * FROM table_user WHERE name = ?1", nativeQuery = true)
List<User> findByUsernameSQL(String name);
基于HQL:
/**
* 基于HQL
* @param name
* @param id
* @return
*/
@Query("Update User set name = ?1 WHERE id = ?2")
@Modifying
int updateNameAndId(String name, Integer id);
在JPA中有三種方式可以進(jìn)行數(shù)據(jù)的查詢(1,方法命名查詢 2,@NamedQuery查詢 3,@Query查詢),
假設(shè)有一張表叫PERSON徽缚,字段:ID(INT),NAME(VARCHAR),AGE(INT),ADDRESS(VARCHAR).
實體類:id(integer),name(String),age(integer),address(String)
- 第一種:方法命名查詢
1) 使用findBy,And關(guān)鍵字
public interface PersonRepository extends Repository<Person, Integer> {
/*
* 通過地址進(jìn)行查詢憨奸,參數(shù)為address,
* 相當(dāng)于JPQL:select p from Person p where p.address=?1
* */
List<Person> findByAddress(String address);
/*
* 通過地址和名字進(jìn)行查詢,參數(shù)為name,address
* 相當(dāng)于JPQL:select p from Person p where p.name=?1 and address=?2
* */
Person findByNameAndAddress(String name,String address);
}
從代碼可以看出凿试,使用findBy,And這樣的關(guān)鍵字排宰,其中的findBy可以用find,getBy,query,read來進(jìn)行代替似芝。
而And就相當(dāng)于sql語句中的and。
2) 用關(guān)鍵字限制結(jié)果數(shù)量板甘,用top和first來實現(xiàn)
*
*查詢符合條件的前十條記錄
*/
List<Person> findFirst10ByName(String name)
/*
*查詢符合條件的前30條記錄
*/
List<Person> findTop30ByName(String name);
- 第二種:@NamedQuery查詢
Spring Data JPA 支持@NameQuery來定義查詢方法党瓮,即一個名稱映射一個查詢語句(要在實體類上寫,不是接口里寫):
@Entity
@NamedQuery(name="Person.findByName",
query="select p from Person p where p.name=?1")
public class Person{
}
這樣子就重新定義了findByName這個方法了盐类。
如果要將多個方法都進(jìn)行重新定義寞奸,可以使用@NameQueries標(biāo)簽,示例如下:
@Entity
@NamedQueries({
@NamedQuery(name="Person.findByName",
query="select p from Person p where p.name=?1"),
@NamedQuery(name = "Person.withNameAndAddressNamedQuery",
query = "select p from Person p where p.name=?1 and address=?2")
})
public class Person{
}
這個時候,接口里定義的findByName方法就是上面的方法了傲醉,不再是方法命名查詢的方法了蝇闭。
- 第三種:@Query查詢
Spring Data JPA 支持@Query來定義查詢方法,使用方法是將@Query寫在接口的方法上面:
public interface PersonRepository extends Repository<Person, Integer> {
@Query("select p from Person p where p.name=?1 and p.address=?2")
Person withNameAndAddressQuery(String name,String address);
}
這里的參數(shù)是根據(jù)索引號來進(jìn)行查詢的硬毕。
當(dāng)然我們也是可以根據(jù)名稱來進(jìn)行匹配呻引,然后進(jìn)行查詢的,示例如下:
public interface PersonRepository extends Repository<Person, Integer> {
@Query("select p from Person p where p.name= :name and p.address= :address")
Person withNameAndAddressQuery(@Param("name")String name,@Param("address")String address);
}
Spring Data JPA支持使用@Modifying和@Query注解組合來進(jìn)行更新查詢吐咳,示例如下:
public interface PersonRepository extends Repository<Person, Integer> {
@Modifying
@Transcational
@Query("update Person p set p.name=?1 ")
int setName(String name);
}
int表示的是更新語句所影響的行數(shù)逻悠。
- 排序查詢
userRepository.findAll(new Sort(new Sort.Order(Sort.Direction.ASC,"id")));
- 分頁查詢
//分頁查詢?nèi)?返回封裝了分頁信息,注意: 0為第一頁韭脊,1為第2頁
Page<User> pageInfo = userRepository.findAll(PageRequest.of(0, 3, Sort.Direction.ASC, "id"));
log.info("總頁數(shù)" + pageInfo.getTotalPages());
log.info("頁大小" + pageInfo.getSize());
log.info("當(dāng)前頁" + pageInfo.getPageable().getPageNumber());
log.info("總記錄數(shù)" + pageInfo.getTotalElements());
//內(nèi)容
List<User> userList = pageInfo.getContent();
- example查詢
User user = new User();
user.setUsername("admin");
Example<User> example = Example.of(user);
List<User> list = userRepository.findAll(example);
log.info(list);
- getOne方法
需添加配置:
#延遲加載
spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true
運行:
# 只可讀取存在的數(shù)據(jù)童谒,不存在拋異常
User user=userRepository.getOne(1L);
- findOne方法
User user=new User();
user.setId(3L);
Example example=Example.of(user);
Optional<User> optionalUser=userRepository.findOne(example);
log.info(optionalUser.get().getUsername());
說明:如果不存在,會報:No value present異常
User user = new User();
user.setId(3L);
Example example = Example.of(user);
Optional<User> optionalUser = userRepository.findOne(example);
//判斷是否存在
if (optionalUser.isPresent()) {
log.info(optionalUser.get().getUsername());
}
或:
//存在即返回, 無則提供默認(rèn)值
User user = new User();
user.setId(id);
Example<User> userExample = Example.of(user);
return userRepository.findOne(userExample).orElse(null);
在查詢時沪羔,通常需要同時根據(jù)多個屬性進(jìn)行查詢饥伊,且查詢的條件也格式各樣(大于某個值、在某個范圍等等)蔫饰,Spring Data JPA 為此提供了一些表達(dá)條件查詢的關(guān)鍵字琅豆,大致如下:
And --- 等價于SQL 中的and 關(guān)鍵字,比如findByUsernameAndPassword(String user, Striang pwd)篓吁;
Or --- 等價于SQL 中的or 關(guān)鍵字茫因,比如findByUsernameOrAddress(String user, String addr);
Between --- 等價于SQL 中的between 關(guān)鍵字杖剪,比如findBySalaryBetween(int max, int min)冻押;
LessThan --- 等價于SQL 中的"<",比如findBySalaryLessThan(int max)盛嘿;
lGreaterThan --- 等價于SQL 中的">"洛巢,比如findBySalaryGreaterThan(int min);
IsNull --- 等價于SQL 中的"is null"次兆,比如findByUsernameIsNull()狼渊;
IsNotNull --- 等價于SQL 中的"is not null",比如findByUsernameIsNotNull();
NotNull --- 與IsNotNull 等價狈邑;
Like --- 等價于SQL 中的"like"城须,比如findByUsernameLike(String user);
NotLike --- 等價于SQL 中的"not like"米苹,比如findByUsernameNotLike(String user)糕伐;
OrderBy --- 等價于SQL 中的"order by",比如findByUsernameOrderBySalaryAsc(String user)蘸嘶;
Not --- 等價于SQL 中的"良瞧! =",比如findByUsernameNot(String user)训唱;
In --- 等價于SQL 中的"in"褥蚯,比如findByUsernameIn(Collection<String> userList) ,方法的參數(shù)可以
是Collection 類型况增,也可以是數(shù)組或者不定長參數(shù)赞庶;
NotIn --- 等價于SQL 中的"not in",比如findByUsernameNotIn(Collection<String> userList) 澳骤,方法的參數(shù)可以是Collection 類型歧强,也可以是數(shù)組或者不定長參數(shù);