使用JPA(Hibernate)操作數據庫
JAP(Java Persisitence API),定義了對象關係映射(ORM)以及實體對象持久化的標準接口。
概述
在spring boot中JPA是依靠Hibernate才得以實現的。
JPA所維護的核心是實體(Entity Bean),而它是通過一個持久化上下文(Persinstence Context)來使用的。持久化上下文包含以下3個部分:
- 對象關係映射(Object Relational Mapping,簡稱ORM,或O/RM,或O/R映射)描述,JPA支持註解或者XML兩種形式的描述,在spring boot中主要通過註解實現。
- 實體操作API,通過規範可以實現對實體對象的CRUD操作,來完成對象的持久化和查詢。
- 查詢語言,約定了面向對象的查詢語言JPQL(Java Persistence Query Language),通過這層關係可以實現比較靈活的查詢。
開發JPA
在Maven中引入spring-boot-starter-data-jpa
<code> <dependency>
<groupid>org.springframework.boot/<groupid>
<artifactid>spring-boot-starter-data-jpa/<artifactid>
/<dependency>/<code>
定義用戶POJO
<code>package com.lay.pojo;
//指明User是一個實體
@Entity(name = "user")
//定義映射的表
@Table(name = "t_user")
public class User {
//表明主鍵
@Id
//策略為遞增
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id = null;
//定義屬性和表的映射關係(屬性和數據庫列名不一致)
@Column(name = "user_name")
private String userName = null;
//定義轉換器
@Convert(converter = SexConverter.class)
private SexEnum sex = null;//枚舉
private String note = null;
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 SexEnum getSex() {
return sex;
}
public void setSex(SexEnum sex) {
this.sex = sex;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
}
/<code>
性別枚舉類SexEnum
<code>package com.lay.enumeration;
public enum SexEnum {
MALE(1, "男"), FEMALE(2, "女");
private int id;
private String name;
SexEnum(int id, String name) {
this.id = id;
this.name = name;
}
public static SexEnum getEnumById(int id) {
for (SexEnum sex : SexEnum.values()) {
if (sex.getId() == id) {
return sex;
}
}
return null;
}
public static SexEnum getEnumByName(String name) {
for (SexEnum sex : SexEnum.values()) {
if (sex.getName().equals(name)) {
return sex;
}
}
return null;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
/<code>
性別轉換器SexConverter
<code>package com.lay.converter;
public class SexConverter implements AttributeConverter<sexenum> {
//將枚舉轉換為數據庫列
@Override
public Integer convertToDatabaseColumn(SexEnum sex) {
return sex.getId();
}
//將數據庫列轉換為枚舉
@Override
public SexEnum convertToEntityAttribute(Integer id) {
return SexEnum.getEnumById(id);
}
}
/<sexenum>/<code>
JPA接口
有了上述POJO對象的定義,我們還需要一個JPA接口來定義對應的操作。為此Spring提供了JpaRepository接口,它本身也繼承了其他接口。
JPA最頂級的接口是Repsitory,而它沒有定義任何方法,定義方法的是它的子接口CrudRepository,其定義的最基本的增刪改查操作,功能性還不足夠強大。為此PagingAndSortingRepository則繼承了它並且提供了分頁和排序的功能,最後JpaRepository擴展了PagingAndSortingRepository,而且還有QueryByExampleExecutor接口,這樣就可以擁有按例子(Example)查詢的功能,一般而言。我們只需要定義JPA擴展接口JpaRepository便可以獲得JPA提供的方法了。
JpaUserRepository
<code>package com.lay.dao;
public interface JpaUserRepository extends JpaRepository<user> {
}
/<user>/<code>
這樣便擁有了系統默認幫我們實現的方法。請注意,這並不需要提供任何實現類,這些spring會根據JPA接口規範幫我們完成。
接口掃描和實體註冊
對於Jpa接口JpaUserRepository,還需要制定Spring boot的掃描路徑,才能將接口掃描到spring ioc容器中。與此同時,我們還要將實體類User的註冊給JPA才能測試這個控制器。用到兩個JPA註解:
- @EnableJpaRepositories:啟用JPA編程
- @EntityScan:對實體Bean掃描
啟動主函數SpringbootDatabaseApplication
<code>package com.lay;
@SpringBootApplication
//定義JPA接口掃描包路徑
@EnableJpaRepositories(basePackages = "com.lay.dao")
//定義實體Bean掃描包路徑
@EntityScan(basePackages = "com.lay.pojo")
public class SpringbootDatabaseApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootDatabaseApplication.class, args);
}
}
/<code>
實際上,即使沒有使用@EnableJpaRepositories和@EntityScan,只要依賴了spring-boot-starter-jpa。spring boot2.x也會對項目進行掃描。
配置文件
application.properties
<code>spring.datasource.url=jdbc:mysql://127.0.0.1:3306/springboot_database
spring.datasource.username=root
spring.datasource.password=123456
# spring.datasource.driver-class-name=com.mysql.jdbc.Driver
#指定數據連接池的類型
spring.datasource.type=org.apache.commons.dbcp2.BasicDataSource
#最大等待連接中的數量,設置0為沒有限
spring.datasource.dbcp2.max-idle=10
#最大連接活動數
spring.datasource.dbcp2.max-total=50
#最大等待毫秒數,單位ms,超過時間會出錯誤信息
spring.datasource.dbcp2.max-wait-millis=10000
#數據庫連接池初始化連接數
spring.datasource.dbcp2.initial-size=5
#使用MySQL數據庫方言
spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect
#打印數據庫sql
spring.jpa.show-sql=true
#選擇hibernate數據定義語言(DDL)策略為update
spring.jpa.hibernate.ddl-auto=update/<code>
控制器測試接口
JpaController
<code>package com.lay.controller;
@Controller
@RequestMapping(value = "/jpa")
public class JpaController {
//注入JPA接口
@Autowired
private JpaUserRepository jpaUserRepository = null;
@RequestMapping("/getUser")
@ResponseBody
public User getUser(Long id) {
//使用JPA接口查詢對象
User user = jpaUserRepository.findById(id).get();
return user;
}
}
/<code>
啟動服務測試,在瀏覽器地址欄輸入http://localhost:8080/jpa/getUser?id=1
日誌打印了sql
<code>Hibernate: select user0_.id as id1_0_0_, user0_.note as note2_0_0_, user0_.sex as sex3_0_0_, user0_.user_name as user_nam4_0_0_ from t_user user0_ where user0_.id=?
/<code>
使用JPA查詢語言JPQL
JPA查詢語言JPQL與Hibernate提供的HQL是十分接近的。這裡使用註解@Query標識語句就可以了。
JpaUserRepository
<code>package com.lay.dao;
public interface JpaUserRepository extends JpaRepository<user> {
@Query("form user where user_name like concat('%',?1,'%')" + "and note like concat('%',?2,'%')")
public List<user> findUsers(String userName, String note);
}
/<user>/<user>/<code>
注意這裡寫from user中的user是定義實體類名稱(@Entity註解的name屬性),所以才能這樣定義一條JPQL,提供給上層調用。
JPA命名查詢
除了可以定義語句查詢,按照一定規則命名的方法也可以在不寫任何代碼的情況下完成邏輯。
<code>package com.lay.dao;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import com.lay.pojo.User;
public interface JpaUserRepository extends JpaRepository<user> {
@Query("form user where user_name like concat('%',?1,'%')" + "and note like concat('%',?2,'%')")
public List<user> findUsers(String userName, String note);
/**
* 按用戶名稱模糊查詢
* @param userName
* @return
* @Date 2018年10月29日 下午4:58:07
* @Author lay
*/
List<user> findByUserNameLike(String userName);
/**
* 根據主鍵查詢
* @param id
* @return
* @Date 2018年10月29日 下午4:58:45
* @Author lay
*/
User getUserById(Long id);
/**
* 按照用戶名稱或者備註進行模糊查詢
* @param userName
* @param note
* @return
* @Date 2018年10月29日 下午4:59:57
* @Author lay
*/
List<user> findByUserNameLikeOrNoteLike(String userName, String note);
}
/<user>/<user>/<user>/<user>/<code>
這裡的命名是以動詞(get/find)開始的,而已by代表按照什麼內容進行查詢。
閱讀更多 Sadlay 的文章