简介
首先理解Spring Date JPA是什么?
SpringData:其实SpringData就是Spring提供了一个操作数据的框架。而SpringData JPA只是SpringData框架下的一个基于JPA规范操作数据的模块。
SpringData JPA:基于JPA的规范数据进行操作。简化操作长久层的代码。只须要编写接口就能够。
JPA是Spring Data下的子项目,JPA是Java Persistence API的简称,中文名为Java长久层API,是JDK 5.0注解或XML形容对象-关系表的映射关系,并将运行期的实体对象长久化到数据库中
你能够了解为JPA和Mybatis是起雷同作用的,都是长久层的框架,然而因为当初Mybatis的广泛应用,当初理解和应用JPA的人较少.
但在我应用的过程中,也发现其一些劣势.
整合
1.导入jar包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
2.yml配置文件
spring:
datasource:
url: jdbc:mysql://localhost:3306/mytest
type: com.alibaba.druid.pool.DruidDataSource
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver //驱动
jpa:
hibernate:
ddl-auto: update //自动更新
show-sql: true //日志中显示sql语句
这里留神:jpa:hibernate:ddl-auto: update
是hibernate的配置属性,其次要作用是:主动创立、更新、验证数据库表构造。该参数的几种配置如下:
1.·create:每次加载hibernate时都会删除上一次的生成的表,而后依据你的model类再从新来生成新表,哪怕两次没有任何扭转也要这样执行,这就是导致数据库表数据失落的一个重要起因。
2.·create-drop:每次加载hibernate时依据model类生成表,然而sessionFactory一敞开,表就主动删除。
3.·update:最罕用的属性,第一次加载hibernate时依据model类会主动建设起表的构造(前提是先建设好数据库),当前加载hibernate时依据model类自动更新表构造,即便表构造扭转了但表中的行依然存在不会删除以前的行。要留神的是当部署到服务器后,表构造是不会被马上建设起来的,是要等利用第一次运行起来后才会。
4.·validate:每次加载hibernate时,验证创立数据库表构造,只会和数据库中的表进行比拟,不会创立新表,然而会插入新值。
我在首次创立时会设为create,创立好后改为validate.
3.实体类
既然上边的参数能够帮忙咱们主动的去通过实体类来创立保护表,那么实体类该怎么写呢,又是怎么建设与表的映射
简略的创立一个实体类:get/set办法由注解实现
@Entity
@Getter
@Setter
@Table(name = "person")
public class Person {
@Id
@GeneratedValue
private Long id;
@Column(name = "name", length = 20)
private String name;
@Column(name = "agee", length = 4)
private int age;
}
创立好实体类并标注好注解后启动主启动类,应该就会在你配置的数据库中主动生成表.
4.Repository接口
personRepository接口如下,
若只是简略的对单表进行crud只须要继承JpaRepository接口,传递了两个参数:1.实体类,2.实体类中主键类型
public interface PersonRepository extends JpaRepository<Person, Long> {
}
然而当然,咱们在工作应用中,不可能只是简略的依据字段查一下就能够了,当你须要传入整个实体类,在依据其中的所有属性进行动静简单查问,那仅仅继承这个接口就不能满足咱们的需要了,
就须要咱们再去继承JpaSpecificationExecutor<T>
该接口,泛型内传入实体类,只有简略实现toPredicate
办法就能够实现简单的查问,
该接口中提供了几个办法:
public interface JpaSpecificationExecutor<T> {
T findOne(Specification<T> spec);
List<T> findAll(Specification<T> spec);
Page<T> findAll(Specification<T> spec, Pageable pageable);
List<T> findAll(Specification<T> spec, Sort sort);
long count(Specification<T> spec);
}
办法中的Specification
就是须要咱们传进去的参数,它是一个接口,也是咱们实现简单查问的要害,其中只有一个办法toPredicate
public interface Specification<T> {
Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb);
}
其api编写能够参考:
http://blog.csdn.net/dracotianlong/article/details/28445725
http://developer.51cto.com/art/200911/162722.htm
5.Controller
而后咱们能够间接在controller中编写代码即可(如果业务简单,当然如果service层也是最好).
简略crud:
@RestController
@RequestMapping(value = "person")
public class PerconController {
@Autowired
private PersonRepository personRepository;
@PostMapping(path = "addPerson")
public void addPerson(Person person) {
personRepository.save(person);
}
@DeleteMapping(path = "deletePerson")
public void deletePerson(Long id) {
personRepository.delete(id);
}
}
简略的crud甚至不须要在Repository中写代码,JpaRepository中已有封装好的间接应用即可.
那么咱们怎么本人去编写一些简略的代码呢?
咱们以依据name查问person为例:
在repository接口中增加如下查询方法:
1.留神办法名肯定是findBy+属性名
Person findByName(String name);
还须要留神依据ID查找的findById是不必本人增加办法的,由接口曾经封装,然而源码中返回的是Optional 类型。那么这个时候该如何取得T 实体类类型呢,只须要get()即可,就是findById(Id).get()
即返回T类型
2.除了增加findBy这种不必写sql的办法外,还有一种能够本人编写sql的办法:
能够在所增加的办法上通过@Query注解,在value属性上写sql语句来实现对数据库的操作,
带参查问:(1、依据参数地位2、依据Param注解)
/**
* 查问依据参数地位
* @param name
* @return
*/
@Query(value = "select * from person where name = ?1",nativeQuery = true)
Person findPersonByName(String Name);
/**
* 查问依据Param注解
* @param name
* @return
*/
@Query(value = "select p from person p where p.uname = :name")
Person findPersonByNameTwo(@Param("name") String name);
置信大家也留神到,在@Query中传入了一个属性nativeQuery,
- @Query有nativeQuery=true,示意可执行的原生sql,原生sql指能够间接复制sql语句给参数赋值就能运行
- @Query无nativeQuery=true, 示意不是原生sql,查问语句中的表名则是对应的我的项目中实体类的类名
这里去理解了一下其生成sql的原理:
其实JPA在这里遵循Convention over configuration(约定大概配置)的准则,遵循spring 以及JPQL定义的办法命名。Spring提供了一套能够通过命名规定进行查问构建的机制。这套机制会把办法名首先过滤一些关键字,比方 find…By, read…By, query…By, count…By 和 get…By 。零碎会依据关键字将命名解析成2个子语句,第一个 By 是辨别这两个子语句的关键词。这个 By 之前的子语句是查问子语句(指明返回要查问的对象),前面的局部是条件子语句。如果间接就是 findBy… 返回的就是定义Respository时指定的畛域对象汇合,同时JPQL中也定义了丰盛的关键字:and、or、Between等等,上面咱们来看一下JPQL中有哪些关键字:
Keyword Sample JPQL snippet
- And—-findByLastnameAndFirstname—-where x.lastname = ?1 and
- Or—-findByLastnameOrFirstname—-where x.lastname = ?1 or x.firstname = ?2
- Is,Equals—-findByFirstnameIs,findByFirstnameEquals—-where x.firstname = ?1
- Between—-findByStartDateBetween—-where x.startDate between ?1 and ?2
- LessThan—-findByAgeLessThan—-where x.age < ?1
- LessThanEqual—-findByAgeLessThanEqual—-where x.age ⇐ ?1
- GreaterThan—-findByAgeGreaterThan—-where x.age > ?1
- GreaterThanEqual—-findByAgeGreaterThanEqual—-where x.age >= ?1
- After—-findByStartDateAfter—-where x.startDate > ?1
- Before—-findByStartDateBefore—-where x.startDate < ?1
- IsNull—-findByAgeIsNull—-where x.age is null
- IsNotNull,NotNull—-findByAge(Is)NotNull—-where x.age not null
- Like—-findByFirstnameLike—-where x.firstname like ?1
- NotLike—-findByFirstnameNotLike—-where x.firstname not like ?1
- StartingWith—-findByFirstnameStartingWith—-where x.firstname like ?1 (parameter bound with appended %)
- EndingWith—-findByFirstnameEndingWith—-where x.firstname like ?1 (parameter bound with prepended %)
- Containing—-findByFirstnameContaining—-where x.firstname like ?1 (parameter bound wrapped in %)
- OrderBy—-findByAgeOrderByLastnameDesc—-where x.age = ?1 order by x.lastname desc
- Not—-findByLastnameNot—-where x.lastname <> ?1
- In—-findByAgeIn(Collection ages)—-where x.age in ?1
- NotIn—-findByAgeNotIn(Collection age)—-where x.age not in ?1
- TRUE—-findByActiveTrue()—-where x.active = true
- FALSE—-findByActiveFalse()—-where x.active = false
- IgnoreCase—-findByFirstnameIgnoreCase—-where UPPER(x.firstame) = UPPER(?1)
简单crud:
简单crud的查问是依附JpaSpecificationExecutor<T>
接口,以及specification的toPredicate
办法来增加条件,上文中也根本介绍过,所以在这里就简略贴一下代码,大家依据例子,应该就能够本人写了:
public List<Flow> queryFlows(int pageNo, int pageSize, String status, String userName, Date createTimeStart, Date createTimeEnd) {
List<Flow> result = null;
// 结构自定义查问条件
Specification<Flow> queryCondition = new Specification<Flow>() {
@Override
public Predicate toPredicate(Root<Flow> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
List<Predicate> predicateList = new ArrayList<>();
if (userName != null) {
predicateList.add(criteriaBuilder.equal(root.get("currentOperator"), userName));
}
if (status != null) {
predicateList.add(criteriaBuilder.equal(root.get("status"), status));
}
if (createTimeStart != null && createTimeEnd != null) {
predicateList.add(criteriaBuilder.between(root.get("createTime"), createTimeStart, createTimeEnd));
}
if (orderId!= null) {
predicateList.add(criteriaBuilder.like(root.get("orderId"), "%" + orderId+ "%"));}
return criteriaBuilder.and(predicateList.toArray(new Predicate[predicateList.size()]));
}
};
// 分页和不分页,这里按起始页和每页展现条数为0时默认为不分页,分页的话按创立工夫降序
if (pageNo == 0 && pageSize == 0) {
result = flowRepository.findAll(queryCondition);
} else {
result = flowRepository.findAll(queryCondition, PageRequest.of(pageNo - 1, pageSize, Sort.by(Sort.Direction.DESC, "createTime"))).getContent();
}
return result;
}
了解了之后其实很简略,上边次要就是两部:1.先将你所须要的条件加到predicate汇合中去,例子中也有equal/between/like相等/区间/含糊,根本也是平时应用的几个,增加好条件后2.进行了分页,判断有没有传入分页的参数,所有传了就分页,没传就查全副,分页中有一个getContent(),能够不加,不加的话还会返回页数/总条数等一些分页的参数,加这个办法就只返回list汇合.
总结
我也是这两天忽然在工作中须要用到JPA,平时也都是应用Mybatis,然而找了很多文章材料,根本也是应用起来曾经没有问题了,也实现了业务,所以根底的应用看着一篇就够了,大家有问题的话也欢送多多斧正!
发表回复