最近mybatis用的多了,都对hibernate这些东西有点生疏了,正好趁着这篇博客补习一下jpa,正好也看看springboot的jpa和hibernate继承的jpa有什么区别。
随着这几年mybaits的流行,人们好像忘了,就在前几年,hibernate还是数据访问的绝对霸主,使用O/R映射技术实现数据库访问。
使用Spring Data Jpa访问数据库十分简单,只是需要简单的继承JpaRespository就可以。
public interface PersonRespository extend JpaRespository<Person,Long>
就可以执行如下操作
public interface JpaRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
//查询全部
List<T> findAll();
//排序查询
List<T> findAll(Sort var1);
//根据id集合查询
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);
<S extends T> List<S> findAll(Example<S> var1);
<S extends T> List<S> findAll(Example<S> var1, Sort var2);
}
都是一些基本的操作,当然这些都是JapRespository的源码的东西,我们就不再一一描述了。
我们创建一个实体来映射数据库的表字段。
添加jar包
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework</groupId>
<artifactId>gs-accessing-data-jpa</artifactId>
<version>0.1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.5.RELEASE</version>
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.14</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-releases</id>
<name>Spring Releases</name>
<url>https://repo.spring.io/libs-release</url>
</repository>
<repository>
<id>org.jboss.repository.releases</id>
<name>JBoss Maven Release Repository</name>
<url>https://repository.jboss.org/nexus/content/repositories/releases</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-releases</id>
<name>Spring Releases</name>
<url>https://repo.spring.io/libs-release</url>
</pluginRepository>
</pluginRepositories>
</project>
Person类
package com.mhb.domain;
import lombok.Data;
import org.hibernate.annotations.NamedQuery;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
/**
* Created by menghaibin on 2017/7/31.
*/
@Data
@Entity
@NamedQuery(name = "Person.withNameAndAddressNamedQuery",
query="select p from Person p where p.name = ?1 and p.address = ?2")
public class Person {
@Id
@GeneratedValue
private Long id;
private String name;
private Integer age;
private String address;
public Person(){
super();
}
public Person(Long id,String name, Integer age, String address ){
super();
this.id = id;
this.name = name;
this.age = age;
this.address = address;
}
}
@Data只是为了方便不写get,set方法,需要的可以添加相应的jar,@Entity指明了这是和数据表映射的实体类。当然我们也可以制定表名字@Entity(name=”person”),NamedQuery我们之后会介绍,@Id标明他是个主键, @GeneratedValue设置主键自增。
大家可能发现这个实体类和以前的实体类配置的不太一样,在hibernate我们大概需要这么配置
//主键,此注解必须要有
@Id
//数据库表中主键列名为id,如果实体类属性名和列名相同,此注解可以省略
@Column(name="id")
//主键的维护策略
@GenericGenerator(name="inc50",strategy="increment")
@GeneratedValue(generator="inc50")
private Integer userID;
但是我们在上边的例子里边也没有发现@Column和@Table这些东西。不注解的时候hibernate回自动根据属性名映射表的字段名字,例如name会映射到NAME,textName会映射到TEXT_NAME,
定义查询方法
创建表和插入数据
我们创建一张简单的表叫做person:
-- auto-generated definition
create table person
(
id int auto_increment
primary key,
name varchar(50) null,
age int null,
address varchar(50) null
);
INSERT INTO person (id, name, age, address) VALUES (1,'mhb','23','zjk');
INSERT INTO person (id, name, age, address) VALUES (2,'bb','23','bj');
INSERT INTO person (id, name, age, address) VALUES (3,'wgl','22','bj');
INSERT INTO person (id, name, age, address) VALUES (4,'lxy','23','bj');
INSERT INTO person (id, name, age, address) VALUES (5,'bb','60','zjk');
INSERT INTO person (id, name, age, address) VALUES (6,'mm','23','zjk');
创建数据库连接
spring.datasource.url=jdbc:mysql://localhost:3306/user_center
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
定义查询方法
/**
* Created by menghaibin on 2017/7/31.
*/
public interface PersonRepository extends JpaRepository<Person, Long> {
List<Person> findByAddress(String name);
Person findByNameAndAddress(String name, String address);
@Query("select p from Person p where p.name = :name and p.address = :address")
Person withNameAndAddressQuery(@Param("name") String name, @Param("address")String address);
Person withNameAndAddressNamedQuery(String name, String address);
}
springboot的jap主要是根据属性名来定义方法,例如findByName,findNameAndAddress,findNameOrAddress,大概就几个关键字,但是我们也可以重新定义一些方法,例如我们根据名字查询的时候,还需要定义年龄是大于23岁的,那么我们就需要用NamedQuery来重新定义这个方法。
//Spring Data JPA 支持@NameQuery来定义查询方法,即一个名称映射一个查询语句(要在实体类上写,不是接口里写),示例如下:
@Entity
@NamedQuery(name="Person.findByName",
query="select p from Person p where p.name=?1 and p.age > 23")
public class Person{
}
当然我们也可以定义多个,
//如果要将多个方法都进行重新定义,可以使用@NameQueries标签,示例如下:
@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{
}
查询方法补充
关键字 | 示例 | 同功能的查询 |
---|---|---|
And | findByTitleAndContent | where x.title = ?1 and x.content = ?2 |
Or | findByTitleOrContent | where x.title = ?1 or x.content = ?2 |
Is, Equals | findByTitle,findByTitleIs,FindByTItleEquals | where x.title = ?1 |
Between | findByCreateTimeBetween | where x.createTime between 1? and ?2 |
LessThanEqual | findByAgeLessThanEqual | where x.age <= ?1 |
LessThan | findByAgeLessThan | where x.age < ?1 |
GreaterThan | findByAgeGreaterThan | where x.age > ?1 |
GreaterThanEqual | findByAgeGreaterThanEqual | where x.age >= ?1 |
After | findByCreateTimeAfter | where x.create > ?1 |
Before | findByCreateTimeBefore | where x.createTime < ?1 |
IsNull | findByAgeIsNull | where x.age is null |
IsNotNull NotNull | findByAgeIsNotNull, findByAgeNotNull | where x.age is not null |
Like | findByTitleLike | where x.title like ?1 |
NotLike | findByTitleNotLike | where x.title not like ?1 |
StartingWith | findByTitleStartingWith | where x.title like ?1(title前加%) |
EndingWith | findByTitleEndingWith | where x.title like ?1(title后加%) |
Containing | findByTitleContaining | where x.title like ?1(前后都加) |
OrderBy | findByTitleOrderByIdDesc | where x.title order by id desc |
Not | findByTitleNot | where title <> ?1 |
In | findByAgeIn(Collection ages) | where x.age in ?1 |
NotIn | findByAgeNotIn(Collection ages) | where x.age not in ?1 |
True | findByActiveTrue | where x.active = true |
False | findByActiveFalse | where x.active = false |
ignoreCase | findByTitleIgnoreCase | where UPPER(x.title) = UPPER(?1) |
创建controller
// Copyright (C), rzsj All Rights Reserved.
package com.mhb.domain;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* Created by menghaibin on 2017/7/31.
*/
@RestController
public class DateController {
@Autowired
PersonRepository personRepository;
@RequestMapping("/save")
public Person save(String name,String address, Integer age){
Person p = personRepository.save(new Person(null, name,age,address));
return p;
}
@RequestMapping("/q1")
public List<Person> q1(String address){
List<Person> p = personRepository.findByAddress(address);
return p;
}
@RequestMapping("/q2")
public Person q2(String name, String address){
Person p = personRepository.findByNameAndAddress(name, address);
return p;
}
@RequestMapping("/q3")
public Person q3(String name, String address){
Person p = personRepository.withNameAndAddressQuery(name, address);
return p;
}
@RequestMapping("/q4")
public Person q4(String name, String address){
Person p = personRepository.withNameAndAddressNamedQuery(name, address);
return p;
}
@RequestMapping("/sort")
public List<Person> sort(){
List<Person> p = personRepository.findAll(new Sort(Sort.Direction.ASC,"age"));
return p;
}
@RequestMapping("/page")
public Page<Person> page(){
Page<Person> page = personRepository.findAll(new PageRequest(1,2));
return page;
}
}
启动项目
我们还是用springboot自带的tomcat启动,创建Application类启动
// Copyright (C), rzsj All Rights Reserved.
package com.mhb.domain;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* Created by menghaibin on 2017/7/31.
*/
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
结果
分页查询
{
"content": [
{
"id": 3,
"name": "wgl",
"age": 22,
"address": "bj"
},
{
"id": 4,
"name": "lxy",
"age": 23,
"address": "bj"
}
],
"totalElements": 6,
"totalPages": 3,
"last": false,
"size": 2,
"number": 1,
"first": false,
"numberOfElements": 2,
"sort": null
}
添加实体
http://localhost:8080/save?name=susan&age=32&address=NewYork
{
"id": 8,
"name": "susan",
"age": 32,
"address": "NewYork"
}
查询方法q2
http://localhost:8080/q2?address=bj&name=wgl
{
"id": 3,
"name": "wgl",
"age": 22,
"address": "bj"
}
小结
这些都是一些jpa的基本功能,至于其他的功能,我们在下一篇博客继续讨论,例如一对一,一对多这些关系映射,还有模糊查询这些应该是我们更加常用到的技术。
要想使用springboot的jpa基本和hibernate差不多,但是要注意添加相应的pom文件,定义方法的时候一定要记住关键字和属性名称一定要对。顺便附上一些常用的方法使用
//And --- 等价于 SQL 中的 and 关键字,比如 findByHeightAndSex(int height,char sex);
public List<User> findByHeightAndSex(int height,char sex);
// Or --- 等价于 SQL 中的 or 关键字,比如 findByHeightOrSex(int height,char sex);
public List<User> findByHeightOrSex(int height,char sex);
//Between --- 等价于 SQL 中的 between 关键字,比如 findByHeightBetween(int min, int max);
public List<User> findByHeightBetween(int min,int max);
//LessThan --- 等价于 SQL 中的 "<",比如 findByHeightLessThan(int max);
public List<User> findByHeightLessThan(int max);
//GreaterThan --- 等价于 SQL 中的">",比如 findByHeightGreaterThan(int min);
public List<User> findByHeightGreaterThan(int min);
//IsNull --- 等价于 SQL 中的 "is null",比如 findByNameIsNull();
public List<User> findByNameIsNull();
//IsNotNull --- 等价于 SQL 中的 "is not null",比如 findByNameIsNotNull();
public List<User> findByNameIsNotNull();
//NotNull --- 与 IsNotNull 等价;
public List<User> findByNameNotNull();
//Like --- 等价于 SQL 中的 "like",比如 findByNameLike(String name);
public List<User> findByNameLike(String name);
//NotLike --- 等价于 SQL 中的 "not like",比如 findByNameNotLike(String name);
public List<User> findByNameNotLike(String name);
//OrderBy --- 等价于 SQL 中的 "order by",比如 findByNameNotNullOrderByHeightAsc();
public List<User>findByNameNotNullOrderByHeightAsc();
//Not --- 等价于 SQL 中的 "! =",比如 findByNameNot(String name);
public List<User> findByNameNot(String name);
//In --- 等价于 SQL 中的 "in",比如 findByNameIN(String name);
public List<User> findByNameIn(String name);
//NotIn --- 等价于 SQL 中的 "not in",比如 findByNameNotIN(String name);
public List<User> findByNameNotIn(String name);
如果项目不是很复杂,不需要很复杂的sql来计算的话,完全可以用springboot jpa来解决战斗。