我們這里使用idea + gradle構建項目,使用spring boot + spring data jpa
接下來看一下spring Data JPA的一些注解;
@Entity 標注用於實體類聲明語句之前,指出該Java 類為實體類,將映射到指定的數據庫表。如聲明一個實體類 Customer,它將映射到數據庫中的 customer 表上。
@Table 當實體類與其映射的數據庫表名不同名時需要使用 @Table 標注說明,該標注與 @Entity 標注並列使用,置於實體類聲明語句之前,可寫於單獨語句行,也可與聲明語句同行。@Table 標注的常用選項是 name,用於指明數據庫的表名,catalog屬性用於指定數據庫實例名,shema作用與catalog一樣,uniqueContraints選項用於設置約束條件
@Id 標注用於聲明一個實體類的屬性映射為數據庫的主鍵列。該屬性通常置於屬性聲明語句之前,可與聲明語句同行,也可寫在單獨行上。
@GeneratedValue 用於標注主鍵的生成策略,通過 strategy 屬性指定。默認情況下,JPA 自動選擇一個最適合底層數據庫的主鍵生成策略:SqlServer 對應 identity,MySQL 對應 auto increment。 在 javax.persistence.GenerationType 中定義了以下幾種可供選擇的策略:
IDENTITY:采用數據庫 ID自增長的方式來自增主鍵字段,Oracle 不支持這種方式;適用於MySQL和SQLServer AUTO:JPA自動選擇合適的策略,是默認選項, 會自動生成一個序列表,通用性比較強。但是MySQL有自增,所以用的比較少 SEQUENCE(序列,適用於Oracle數據庫):通過序列產生主鍵,通過 @SequenceGenerator注解指定序列名,MySql 不支持這種方式 TABLE:通過表產生主鍵,框架借由表模擬序列產生主鍵,使用該策略可以使應用更易於數據庫移植。
@Column 當實體的屬性與其映射的數據庫表的列不同名時需要使用@Column 標注說明,該屬性通常置於實體的屬性聲明語句之前,還可與 @Id 標注一起使用。
-
@Column 標注的常用屬性是 name,用於設置映射數據庫表的列名。此外,該標注還包含其它多個屬性,如:unique 、nullable、length 等。
-
@Column 標注的 columnDefinition 屬性: 表示該字段在數據庫中的實際類型.通常 ORM 框架可以根據屬性類型自動判斷數據庫中字段的類型,但是對於Date類型仍無法確定數據庫中字段類型究竟是DATE,TIME還是TIMESTAMP.此外,String的默認映射類型為VARCHAR, 如果要將 String 類型映射到特定數據庫的 BLOB 或TEXT 字段類型.
@Column標注也可置於屬性的getter方法之前
@Basic 表示一個簡單的屬性到數據庫表的字段的映射,對於沒有任何標注的 getXxxx() 方法,默認即為
@Basic fetch: 表示該屬性的讀取策略,有 EAGER 和 LAZY 兩種,分別表示主支抓取和延遲加載,默認為 EAGER.
optional:表示該屬性是否允許為null, 默認為true
@Transient
在核心的 Java API 中並沒有定義 Date 類型的精度(temporal precision).而在數據庫中,表示 Date 類型的數據有 DATE, TIME, 和 TIMESTAMP 三種精度(即單純的日期,時間,或者兩者 兼備). 在進行屬性映射時可使用@Temporal注解來調整精度。
項目構建直接使用idea + gradle構建項目
build.gradle文件
plugins {
id 'org.springframework.boot' version '2.2.5.RELEASE'
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
id 'java'
}
group = 'com.yang'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '13'
repositories {
maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }
maven{ url 'http://maven.aliyun.com/nexus/content/repositories/jcenter'}
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
compile('mysql:mysql-connector-java')
compile('org.springframework.boot:spring-boot-starter-web')
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
}
test {
useJUnitPlatform()
}
首先看一下application.yml
spring: datasource: # mysql 8.0+ 需要使用帶cj的驅動 driverClassName: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/jpa?useUnicode=true&characterEncoding=utf8 username: root password: mysql jpa: hibernate: # 這個就是更新表,如果字段變化就更新,沒有就不更新,create 就是不管什么情況都會刪除原來的表並新建 ddlAuto: update showSql: true server: port: 8090
package com.yang.demo.entity; import javax.persistence.*; import java.util.Date; /** * User表 */ @Entity @Table(name = "user") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) // 設置自增 private Long id; // 映射字段如果跟數據庫一致可以不用寫 private String username; // name 指定與數據庫中字段的映射關系,unique是否唯一,nullable是否可為空, 可以指定長度,string默認字符串長度是255 // columnDefinition 指定類型 String映射到數據庫中字段就是varchar @Column(name = "u_sex", unique = false, nullable = true, length = 20, columnDefinition = "text") private String sex; // 時間精確到天,默認格式是datetime,時間精確到秒,也可以修改為時間戳 @Temporal(TemporalType.DATE) private Date createTime; // 這個字段就是不與數據庫進行映射 @Transient private String noUse; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } public String getNoUse() { return noUse; } public void setNoUse(String noUse) { this.noUse = noUse; } @Override public String toString() { return "User{" + "id=" + id + ", username='" + username + '\'' + ", sex='" + sex + '\'' + ", createTime=" + createTime + ", noUse='" + noUse + '\'' + '}'; } }
接下來,看一下這些實現方法,首先,spring data jpa 已經為我們封裝好sql,我們只需要在dao層繼承接口,就可以進行使用
package com.yang.demo.dao; import com.yang.demo.entity.User; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; public interface UserDao extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> { }
JpaRepository中,User是實體類,Long是主鍵ID的類型,我們首先看一下JpaRepository的繼承關系圖,接下來看一下這些接口,並測試一下。
@NoRepositoryBean public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> { /* * 獲取所有 */ @Override List<T> findAll(); /* * 獲取所有,可以進行排序 */ @Override List<T> findAll(Sort sort); /* * 獲取所有傳入ID的對象 */ @Override List<T> findAllById(Iterable<ID> ids); /* * 保存所有 */ @Override <S extends T> List<S> saveAll(Iterable<S> entities); /** * 刷新 */ void flush(); /** * Saves an entity and flushes changes instantly. * * 當發生改變我們沒有提交,可以幫助刷新*/ <S extends T> S saveAndFlush(S entity); /** * * @param entities 根據傳入的對象刪除 */ void deleteInBatch(Iterable<T> entities); /** * 刪除數據庫中這個實體對應表的所有數據. */ void deleteAllInBatch(); /** * 根據id查詢出來的對象不為null,但是對象里面的所有屬性全部為null*/ T getOne(ID id); /* * 根據條件查詢 */ @Override <S extends T> List<S> findAll(Example<S> example); /* * 根據條件以及排序規則進行查詢 */ @Override <S extends T> List<S> findAll(Example<S> example, Sort sort); }
接下來進行測試
package com.yang.demo; import com.yang.demo.dao.UserDao; import com.yang.demo.entity.User; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.domain.Example; import org.springframework.data.domain.Sort; import java.util.ArrayList; import java.util.Date; import java.util.List; @SpringBootTest class DemoApplicationTests { @Autowired UserDao userDao; @Test public void testJpaRepository() { // 查找所有 // Hibernate: select user0_.id as id1_0_, user0_.create_time as create_t2_0_, user0_.u_sex as u_sex3_0_, user0_.username as username4_0_ // from user user0_ List<User> users = userDao.findAll(); for (User user : users) { System.out.println(user); // User{id=7, username='xiao ming', sex='男', createTime=2020-02-29, noUse='null'} // User{id=8, username='小紅', sex='女', createTime=2020-02-29, noUse='null'} } // 獲取所有,根據id降序 Sort.Order order = new Sort.Order(Sort.Direction.DESC, "id"); ArrayList<Sort.Order> orders = new ArrayList<>(); orders.add(order); Sort sort = Sort.by(orders); // Hibernate: select user0_.id as id1_0_, user0_.create_time as create_t2_0_, user0_.u_sex as u_sex3_0_, user0_.username as username4_0_ // from user user0_ order by user0_.id desc List<User> userSorts = userDao.findAll(sort); for (User userSort : userSorts) { System.out.println(userSort); //User{id=8, username='小紅', sex='女', createTime=2020-02-29, noUse='null'} //User{id=7, username='xiao ming', sex='男', createTime=2020-02-29, noUse='null'} } // 根據ID查詢 List<Long> ids = new ArrayList<>(); ids.add(1L); ids.add(2L); // Hibernate: select user0_.id as id1_0_, user0_.create_time as create_t2_0_, user0_.u_sex as u_sex3_0_, user0_.username as username4_0_ // from user user0_ where user0_.id in (? , ?) List<User> allById = userDao.findAllById(ids); for (User userById : allById) { System.out.println(userById); // none } // 保存所有 User user2 = new User(); user2.setUsername("xiao ming"); user2.setCreateTime(new Date()); user2.setSex("男"); User user3 = new User(); user3.setUsername("小紅"); user3.setCreateTime(new Date()); user3.setSex("女"); List<User> userList = new ArrayList<>(); userList.add(user2); userList.add(user3); // Hibernate: insert into user (create_time, u_sex, username) values (?, ?, ?) // Hibernate: insert into user (create_time, u_sex, username) values (?, ?, ?) List<User> users1 = userDao.saveAll(userList); for (User user1 : users1) { System.out.println(user1); //User{id=9, username='xiao ming', sex='男', createTime=Sun Mar 01 10:58:08 CST 2020, noUse='null'} //User{id=10, username='小紅', sex='女', createTime=Sun Mar 01 10:58:08 CST 2020, noUse='null'} } // 根據傳入對象刪除,無返回對象 // userDao.deleteInBatch(users1); // 根據ID查詢,如果查詢不到就會報錯 // User one = userDao.getOne(10L); // System.out.println(one); // 查詢,根據對象的示例 // 設置實例 User user = new User(); // 設置查詢條件 user.setUsername("xiao ming"); // 定義example Example<User> example = Example.of(user); // select user0_.id as id1_0_, user0_.create_time as create_t2_0_, user0_.u_sex as u_sex3_0_, user0_.username as username4_0_ // from user user0_ where user0_.username=? List<User> all = userDao.findAll(example); for (User user4 : all) { System.out.println(user4); // User{id=7, username='xiao ming', sex='男', createTime=2020-02-29, noUse='null'} // User{id=9, username='xiao ming', sex='男', createTime=2020-02-29, noUse='null'} } } }
接下來看一下PagingAndSortingRepository
@NoRepositoryBean public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> { /** * 這個JpaRepository重寫了這個方法,就會根據排序規則查詢所有 */ Iterable<T> findAll(Sort sort); /** * 分頁查詢*/ Page<T> findAll(Pageable pageable); }
接下來,我們看一下分頁查詢
@Test public void testPagingAndSort() { // 這個翻頁需要設定排序規則,注意字段需要與entity的類一致 Sort.Order order = new Sort.Order(Sort.Direction.DESC, "createTime"); List<Sort.Order> orders = new ArrayList<>(); orders.add(order); // sort需要傳入的是集合 Sort sort = Sort.by(orders); // 查詢需求 // 第2頁 int pageIndex = 2; // 每頁大小 int pageSize = 2; Pageable pageable = PageRequest.of(pageIndex - 1, pageSize, sort); Page<User> all = userDao.findAll(pageable); System.out.println("總記錄: " + all.getTotalElements()); // 4 System.out.println("總頁數: " + all.getTotalPages()); // 2 System.out.println("當前第幾頁: : " + all.getNumber()); // 1 從0開始 System.out.println("當前頁條數: : " + all.getNumberOfElements()); // 2 for (User user : all) { System.out.println(user); // User{id=9, username='xiao ming', sex='男', createTime=2020-02-29, noUse='null'} // User{id=10, username='小紅', sex='女', createTime=2020-02-29, noUse='null'} } }
接下來看一下CrudRepository里面的方法
@NoRepositoryBean public interface CrudRepository<T, ID> extends Repository<T, ID> { /** * 保存 */ <S extends T> S save(S entity); /** * 保存所有,跟JPARespository中一樣 */ <S extends T> Iterable<S> saveAll(Iterable<S> entities); /** * 根據ID查詢,查不到不會報錯 */ Optional<T> findById(ID id); /** * 根據ID查詢是否存在 */ boolean existsById(ID id); /** * 查詢所有 */ Iterable<T> findAll(); /** * 根據ID可迭代對象查詢 */ Iterable<T> findAllById(Iterable<ID> ids); /** * 查詢這個表中一共多少條 */ long count(); /** * 根據ID刪除 */ void deleteById(ID id); /** * 根據對象刪除 */ void delete(T entity); /** * 根據傳入的對象刪除 */ void deleteAll(Iterable<? extends T> entities); /** * 刪除整個表數據 */ void deleteAll(); }
這些都是一些基礎的方法,比較簡單,來看一下
@Test public void testCurdRepository() { // 保存 User user = new User(); user.setUsername("小明1"); user.setSex("男"); user.setCreateTime(new Date()); // Hibernate: insert into user (create_time, u_sex, username) values (?, ?, ?) User user1 = userDao.save(user); // User{id=12, username='小明1', sex='男', createTime=Sun Mar 01 12:01:35 CST 2020, noUse='null'} System.out.println(user1); // 保存所有 user1.setUsername("小明2"); User user2 = new User(); user2.setUsername("小明3"); user2.setSex("男"); user2.setCreateTime(new Date()); List<User> userList = new ArrayList<>(); userList.add(user1); userList.add(user1); // 連續添加兩個user1 List<User> userList1 = userDao.saveAll(userList); // 從輸出結果可以看出,逐條保存,如果有ID就只是更新,不是保存 for (User user3 : userList1) { System.out.println(user3); // User{id=12, username='小明2', sex='男', createTime=Sun Mar 01 12:01:35 CST 2020, noUse='null'} //User{id=12, username='小明2', sex='男', createTime=Sun Mar 01 12:01:35 CST 2020, noUse='null'} } // 根據ID查詢 // Hibernate: select user0_.id as id1_0_0_, user0_.create_time as create_t2_0_0_, user0_.u_sex as u_sex3_0_0_, user0_.username as username4_0_0_ // from user user0_ where user0_.id=? Optional<User> user4 = userDao.findById(100L); // Optional.empty System.out.println(user4); // 根據Id判斷是否存在 // Hibernate: select count(*) as col_0_0_ from user user0_ where user0_.id=? System.out.println(userDao.existsById(100L)); // false // 查詢所有 // Hibernate: select user0_.id as id1_0_, user0_.create_time as create_t2_0_, user0_.u_sex as u_sex3_0_, user0_.username as username4_0_ // from user user0_ List<User> users = userDao.findAll(); for (User user3 : users) { System.out.println(user3); //User{id=7, username='xiao ming', sex='男', createTime=2020-02-29, noUse='null'} //User{id=8, username='小紅', sex='女', createTime=2020-02-29, noUse='null'} //User{id=9, username='xiao ming', sex='男', createTime=2020-02-29, noUse='null'} //User{id=10, username='小紅', sex='女', createTime=2020-02-29, noUse='null'} //User{id=11, username='小明2', sex='男', createTime=2020-02-29, noUse='null'} //User{id=12, username='小明2', sex='男', createTime=2020-02-29, noUse='null'} } // 查詢總數 // Hibernate: select count(*) as col_0_0_ from user user0_ System.out.println("總數:" + userDao.count()); // 6 // 根據傳入的ID刪除 // 查找id為7的 Optional<User> user3 = userDao.findById(8L); System.out.println(user3); // 數據不存在會報錯 userDao.deleteById(8L); System.out.println(userDao.existsById(8L)); // 根據傳入對象刪除 // getOne是懶加載,如果直接刪除這個對象會報錯,需要使用,因此使用findById // User user5 = userDao.getOne(9L); // findById 返回結果是Optional<T>,調用get即刻返回對象 // Hibernate: select user0_.id as id1_0_0_, user0_.create_time as create_t2_0_0_, user0_.u_sex as u_sex3_0_0_, user0_.username as username4_0_0_ // from user user0_ where user0_.id=? User user5 = userDao.findById(9L).get(); System.out.println(user5); // Hibernate: delete from user where id=? userDao.delete(user5); System.out.println(userDao.existsById(9L)); // 刪除所有 // 看一下表中數據 System.out.println(userDao.count()); // 6 //Hibernate: delete from user where id=? //Hibernate: delete from user where id=? //Hibernate: delete from user where id=? //Hibernate: delete from user where id=? //Hibernate: delete from user where id=? //Hibernate: delete from user where id=? userDao.deleteAll(); // 再看一下表中數據 System.out.println(userDao.count()); // 0 }
上面是常用的,但是感覺還是缺點什么,於是作者又搞了一個接口QueryByExampleExecutor
public interface QueryByExampleExecutor<T> { /** * 根據傳入的條件查詢一個 */ <S extends T> Optional<S> findOne(Example<S> example); /** * 根據傳入的條件查詢所有 */ <S extends T> Iterable<S> findAll(Example<S> example); /** * 根據查詢條件以及排序規則查詢所有 */ <S extends T> Iterable<S> findAll(Example<S> example, Sort sort); /** * 根據查詢條件以及分頁規則,查詢 */ <S extends T> Page<S> findAll(Example<S> example, Pageable pageable); /** * 根據查詢條件,進行計數 */ <S extends T> long count(Example<S> example); /** * 根據查詢條件,判斷表中是否存在 */ <S extends T> boolean exists(Example<S> example); }
接下來看一下測試
@Test public void testQueryByExampleExecutor() { // 測試數據 List<User> all = userDao.findAll(); for (User user : all) { System.out.println(user); // User{id=18, username='xiao ming', sex='男', createTime=2020-02-29, noUse='null'} //User{id=19, username='小紅', sex='女', createTime=2020-02-29, noUse='null'} //User{id=20, username='xiao ming', sex='男', createTime=2020-02-29, noUse='null'} //User{id=21, username='小寧', sex='男', createTime=2020-02-29, noUse='null'} //User{id=22, username='小紅', sex='女', createTime=2020-02-29, noUse='null'} //User{id=23, username='小美', sex='女', createTime=2020-02-29, noUse='null'} //User{id=24, username='小梅', sex='女', createTime=2020-02-29, noUse='null'} } // 設置查詢條件 User user = new User(); user.setId(18L); Example<User> of = Example.of(user); // 根據傳入條件查詢一個,如果不是唯一的會報錯 // Hibernate: select user0_.id as id1_0_, user0_.create_time as create_t2_0_, user0_.u_sex as u_sex3_0_, user0_.username as username4_0_ // from user user0_ where user0_.id=18 Optional<User> one = userDao.findOne(of); // Optional[User{id=18, username='xiao ming', sex='男', createTime=2020-02-29, noUse='null'}] System.out.println(one); // 根據傳入條件查詢所有 // 設置查詢條件 User user1 = new User(); user1.setUsername("小紅"); Example<User> of1 = Example.of(user1); // Hibernate: select user0_.id as id1_0_, user0_.create_time as create_t2_0_, user0_.u_sex as u_sex3_0_, user0_.username as username4_0_ // from user user0_ where user0_.username=? List<User> all1 = userDao.findAll(of1); for (User user2 : all1) { System.out.println(user2); // User{id=19, username='小紅', sex='女', createTime=2020-02-29, noUse='null'} // User{id=22, username='小紅', sex='女', createTime=2020-02-29, noUse='null'} } // 根據條件技術 // Hibernate: select count(user0_.id) as col_0_0_ from user user0_ where user0_.id=18 System.out.println(userDao.count(of)); //1 // Hibernate: select count(user0_.id) as col_0_0_ from user user0_ where user0_.username=? System.out.println(userDao.count(of1)); //2 // 根據查詢條件判斷是否存在 // Hibernate: select user0_.id as id1_0_, user0_.create_time as create_t2_0_, user0_.u_sex as u_sex3_0_, user0_.username as username4_0_ from user user0_ where user0_.id=18 System.out.println(userDao.exists(of)); // true // Hibernate: select user0_.id as id1_0_, user0_.create_time as create_t2_0_, user0_.u_sex as u_sex3_0_, user0_.username as username4_0_ from user user0_ where user0_.username=? System.out.println(userDao.exists(of1)); // true // 查詢需求 // 第2頁 int pageIndex = 1; // 每頁大小 int pageSize = 1; // 排序 Sort.Order order = new Sort.Order(Sort.Direction.DESC, "id"); ArrayList<Sort.Order> orders = new ArrayList<>(); orders.add(order); Sort sort = Sort.by(orders); Pageable pageable = PageRequest.of(pageIndex - 1, pageSize, sort); // 根據查詢條件以及分頁規則查詢 // Hibernate: select user0_.id as id1_0_, user0_.create_time as create_t2_0_, user0_.u_sex as u_sex3_0_, user0_.username as username4_0_ // from user user0_ where user0_.username=? order by user0_.id desc limit ? Page<User> all2 = userDao.findAll(of1, pageable); System.out.println("總記錄: " + all2.getTotalElements()); // 2 System.out.println("總頁數: " + all2.getTotalPages()); // 2 System.out.println("當前第幾頁: : " + all2.getNumber()); // 0 從0開始 System.out.println("當前頁條數: : " + all2.getNumberOfElements()); // 1 for (User user2 : all2) { System.out.println(user2); // User{id=22, username='小紅', sex='女', createTime=2020-02-29, noUse='null'} } }
這個就是大體算法,我們還可以使用自定義sql,注解方式,只要保持jpa規范就好,至於一對一,一對多,多對多就是@anyToMany之類的,一般都不使用,因為有外鍵不舒服,一般做邏輯一致。
另外對於對於多表操作,一定需要開啟事務。