- 首先 JPA(Java Persistence API)和Spring Data是两个范畴的概念, JPA
和Hibernate的关系就像JDBC
域JDBC驱动的关系,即JPA制定了ORM规范,Hibernate是这些规范的实现,因此从功能上来讲JPA相当于Hibernate的一个子集 - Spring Data是Spring 的一个子项目,致力于简化数据库的访问,通过规范的方法名来分析开发者的意图,本实例步骤如下:
step.1
创建数据库jpa:
CREATE DATABASE `jpa` CHARACTER SET 'utf8';
step.2
创建spring boot 项目,添加mysql 和spring data jpa 的倚赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.9</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
step.3
数据库配置:
- 在application.properties中配置数据库的基本信息以及jpa相关配置
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.url=jdbc:mysql:///jpa?useUnicode=true&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=root
spring.jpa.show-sql=true
spring.jpa.database=mysql
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL57Dialect
- 1~4行是数据库基本信息
- 第5行表示是否在控制台打印jpa执行过程生成的sql
- 第6行表示jpa对应的数据库是mysql
- 第7行表示在项目启动时根据实体类更新数据库中的表
step.4
创建实体类Book:
@Entity(name = "t_book")
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "book_name",nullable = false)
private String name;
private String author;
private Float price;
@Transient
private String description;
//省略getter/setter
- @Entity注解表示该类是一个实体类,项目启动时会自动创建一张表,表的名称就是后面name的值,如果没有定义name,那么表名将默认使用类名
- @Id 表示主键,GeneratedValue表示主键自动生成,strategy 表示生成策略
- @Column表示注解可以定制字段的属性
- @Transient表示自动生成表时忽略改字段
step.5
创建BookDao接口,继承JpaRepository
public interface BookDao extends JpaRepository<Book,Integer>{
List<Book> getBooksByAuthorStartingWith(String author);
List<Book> getBooksByPriceGreaterThan(Float price);
@Query(value = "select * from t_book where id=(select max(id) from t_book)",nativeQuery = true)
Book getMaxIdBook();
@Query("select b from t_book b where b.id=:id and b.author=:author")
List<Book> getBookByIdAndAuthor(@Param("author") String author, @Param("id") Integer id);
@Query("select b from t_book b where b.id=?2 and b.name like %?1%")
List<Book> getBooksByIdAndName(String name, Integer id);
}
解释如下:
- 自定义BookDao 继承JpaRepository,其中JpaRepository中已经提供了一些基本的如增删改查的基本数据操作方法
- 第二行定义的方法表示查询以某个字符开始的所有书
- 第三行定义的方法表示查询单价大于某个值的所有书
- 在Spring Data Jpa中只要方法的定义符合规范,就是一定的方法命名规则,支持的命名规则如下表所示:
KeyWords | 方法命名举例 | 对应的SQL |
---|---|---|
And | findByNameAndAge | where name = ? and age = ? |
Or | findByNameOrAge | where name = ? or age = ? |
Is | findByAgeIs | where age = ? |
Equals | findByIdEquals | where id=? |
Between | findByAgeBetween | where age between ? and ? |
LessThan | findByAgeLessThan | where age <? |
GreaterThan | findByAgeGreaterThan | where age >? |
GreaterThanEquals | findByAgeGreaterThanEquals | where age >=? |
LessThanEquals | findByAgeLessThanEquals | where age <=? |
After | findByAgeAfter | where age >? |
Before | findByAgeBefore | where age <? |
IsNull | findByNameIsNull | where name is null |
isNotNull,NotNUll | findByNameNotNull | where name is not null |
Not | findByGenderNot | where gender <>? |
NotIn | findByAgeNotIn | where age not in (?) |
NotLike | findByNameNotLike | where name not like ? |
Like | findByNameLike | where name like ? |
StartingWith | findByNameStartingWith | where name like ‘?%’ |
EndingWith | findByNameEndingWith | where name like ‘%?’ |
Containing,Contains | findByNameContaining | where name like ‘?%?’ |
OrderBy | findByAgeGreaterThanOrderByIdDesc | where age >? order by id desc |
True | findByEnabledTrue | where enabled =true |
False | findByEnabled | Falsewhere enabled =false |
IgnoreCase | findByNameIgnoreCase | where UPPER(name)=UPPER(?) |
- 既定的方法名不一定能满足所有的开发需求,因此Spring Data JPA 也支持自定义JPQL (java persistence query language)或者原生sql,第4~6行表示查询id最大的书,nativeQuery = true表示使用原生的sql查询
- 第7~9行表示根据id和author进行查询,这里使用默认的JPQL进行查询,通过类名和属性名进行访问,而不是通过表明和表的属性,使用:id,:name进行参数绑定,需要注意的是这里使用的列名是属性的名称而不是数据库中列的名称
- 第10,11行也是自定义JPQL查询,不同的是传参方式使用?1,?这种方式
- 如果BokDao中涉及修改操作,则需要添加@Modifying注解并添加事务。
Step. 6
创建BookService:
@Service
public class BookService {
@Autowired
BookDao bookDao;
public void addBook(Book book) {
bookDao.save(book);
}
public Page<Book> getBookByPage(Pageable pageable) {
return bookDao.findAll(pageable);
}
public List<Book> getBooksByAuthorStartingWith(String author){
return bookDao.getBooksByAuthorStartingWith(author);
}
public List<Book> getBooksByPriceGreaterThan(Float price){
return bookDao.getBooksByPriceGreaterThan(price);
}
public Book getMaxIdBook(){
return bookDao.getMaxIdBook();
}
public List<Book> getBookByIdAndAuthor(String author, Integer id){
return bookDao.getBookByIdAndAuthor(author, id);
}
public List<Book> getBooksByIdAndName(String name, Integer id){
return bookDao.getBooksByIdAndName(name, id);
}
}
- 其中第六行的save方法建对象数据保存到数据库save方法是由JpaRepository接口提供的
- 第九行是一个分页查询,fandAll方法的返回值是Page< Book>
step.7
创建BookController:
@RestController
public class BookController {
@Autowired
BookService bookService;
@GetMapping("/findAll")
public void findAll() {
PageRequest pageable = PageRequest.of(2, 3);
Page<Book> page = bookService.getBookByPage(pageable);
System.out.println("总页数:"+page.getTotalPages());
System.out.println("总记录数:"+page.getTotalElements());
System.out.println("查询结果:"+page.getContent());
System.out.println("当前页数:"+(page.getNumber()+1));
System.out.println("当前页记录数:"+page.getNumberOfElements());
System.out.println("每页记录数:"+page.getSize());
}
@GetMapping("/search")
public void search() {
List<Book> bs1 = bookService.getBookByIdAndAuthor("鲁迅", 7);
List<Book> bs2 = bookService.getBooksByAuthorStartingWith("吴");
List<Book> bs3 = bookService.getBooksByIdAndName("西", 8);
List<Book> bs4 = bookService.getBooksByPriceGreaterThan(30F);
Book b = bookService.getMaxIdBook();
System.out.println("bs1:"+bs1);
System.out.println("bs2:"+bs2);
System.out.println("bs3:"+bs3);
System.out.println("bs4:"+bs4);
System.out.println("b:"+b);
}
@GetMapping("/save")
public void save() {
Book book = new Book();
book.setAuthor("鲁迅");
book.setName("呐喊");
book.setPrice(23F);
bookService.addBook(book);
}
}
- 在findAll中首先需要调用PageRequest 中的of方法构造PageRequest
对象,of方法接受两个参数,第一个是页数,从0开始,第二个是每页显示的条数 - 在save接口中构造一个Book对象,直接调用save方法进行保存
step.8
测试:
加入数据
- 输入http://localhost:8080/findAll,结果如下
总页数:2
总记录数:4
查询结果:[Book{id=1, name='三国演义', author='罗贯中', price=99.0, description='null'}, Book{id=2, name='西游记', author='六小龄童', price=999.0, description='null'}, Book{id=3, name='鸡你太美', author='cxk', price=9999.0, description='null'}]
当前页数:1
当前页记录数:3
每页记录数:3
- 接下来调用http://localhost:8080/save接口,调用后数据更新如图:
- 接下来调用http://localhost:8080/search 接口,查询结果如下:
bs1:[Book{id=3, name='鸡你太美', author='cxk', price=9999.0, description='null'}]
bs2:[Book{id=6, name='呐喊', author='鲁迅', price=23.0, description='null'}]
bs3:[Book{id=2, name='西游记', author='六小龄童', price=999.0, description='null'}]
bs4:[Book{id=1, name='三国演义', author='罗贯中', price=99.0, description='null'}, Book{id=2, name='西游记', author='六小龄童', price=999.0, description='null'}, Book{id=3, name='鸡你太美', author='cxk', price=9999.0, description='null'}, Book{id=6, name='呐喊', author='鲁迅', price=23.0, description='null'}]
b:Book{id=6, name='呐喊', author='鲁迅', price=23.0, description='null'}
注意:其中数据源的不同可能会导致查询不到结果等问题,可在BookDao中自行改动数据库语句