JPA基础概述
JPA概念
JPA是Java的一个规范。 它用于在Java对象和关系数据库之间保存数据。 JPA充当面向对象的领域模型和关系数据库系统之间的桥梁。
由于JPA只是一个规范,它本身不执行任何操作。 它需要一个实现。 因此,像Hibernate,TopLink和iBatis这样的ORM工具实现了JPA数据持久性规范。
JPA仅仅是一种规范,也就是说JPA仅仅定义了一些接口,而接口是需要实现才能工作的。所以底层需要某种实现,而Hibernate就是实现了JPA接口的ORM框架
传统jdbc存在的问题
- 数据库连接频繁的创建和关闭,缺点是浪费数据库资源,影响操作效率。
解决方法:使用数据库连接池 - sql语句是硬编码,如果需求变更需要修改sql,就需要修改java代码,需要重新编译,系统不易维护。
解决方法:将sql语句 统一配置在文件中,修改sql不需要修改java代码 - 通过prepareStatement向占位符设置参数,存在硬编码(参数位置,参数)问题。系统不易维护。
解决方法:将sql中的占位符及对应的参数类型配置在配置文件中,能过自动输入到映射 - 遍历查询结果集存在硬编码(列名)
解决方法:通过进行 sql查询结果向java对象映射,能过自动输出到映射
ORM概述
orm(Object Relation Mapping)对象关系映射
ORM, 解决了:
- 存储: 将对象直接存入数据库
- 获取: 直接从数据库中拿去对象
JPA的优缺点
优点
- 标准化: 提供相同的 API,这保证了基于JPA 开发的企业应用能够经过少量的修改就能够在不同的 JPA 框架下运行。
- 简单易用,集成方便: JPA 的主要目标之一就是提供更加简单的编程模型,在 JPA 框架下创建实体和创建 Java 类一样简单,只需要使用 javax.persistence.Entity 进行注释;JPA 的框架和接口也都非常简单,
- 可媲美JDBC的查询能力: JPA的查询语言是面向对象的,JPA定义了独特的JPQL,而且能够支持批量更新和修改、JOIN、GROUP BY、HAVING 等通常只有 SQL 才能够提供的高级查询特性,甚至还能够支持子查询。
- 支持面向对象的高级特性: JPA 中能够支持面向对象的高级特性,如类之间的继承、多态和类之间的复杂关系,最大限度的使用面向对象的模型。
缺点
- 性能上比较差:应用底层要把对象翻译成原始sql,然后连接数据库查询。
- 不适合复杂查询:复杂查询使用对象映射很不方便,也很复杂,更不容易维护。
SpringDataJPA简介
就是Spring的数据持久化对jpa做了集成,使得jpa使用起来更简单,开发效率更高,单表的增删改查操作都已经实现好了,我们只要调用方法就可以。
初识 SpringDataJPA
pom.xml依赖
<!-- 导入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>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.22</version>
</dependency>
yml文件
server:
port: 8010
spring:
datasource:
url: jdbc:mysql://192.168.137.233:3306/jpa?useUnicode=true&characterEncoding=utf-8
username: root
password: root
jpa:
show-sql: true
hibernate:
ddl-auto: update
用户实体类
@Data
@Entity
@Table(name="tb_user")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// 用户名
private String username;
// 密码
private String password;
// 电话
private String phone;
// 盐
private String salt;
// 创建时间
private Date created;
}
UserDao
@Component
public class UserDao {
@Autowired
private EntityManager entityManager;
public List<User> findAll(){
Query query = entityManager.createQuery("FROM User u");
List resultList = query.getResultList();
return resultList;
}
public User findById(Long id){
return entityManager.find(User.class,id);
}
public List<User> findNameLike(String str){
String jpql = "FROM User u WHERE u.username like :username";
Query query = entityManager.createQuery(jpql);
query.setParameter("username","%"+str+"%");
List resultList = query.getResultList();
return resultList;
}
public Object[] nativeQueryById(Long id){
String sql = "select * from tb_user where id = ?";
Query query = entityManager.createNativeQuery(sql);
query.setParameter(1,id);
return (Object[])query.getSingleResult();
}
@Transactional
public User save(User user){
return entityManager.merge(user);
}
/**
* 1.remove方法只能移除执久化对象,不能删除游离对象 即删除的对象必须是通过jpa查询出来的对象
* 2.jpa查询出来的对象必须和remove方法是在一个事务下完成,否则报错
* @param id
*/
@Transactional
public void delete(Long id){
User user = findById(id);
entityManager.remove(user);
}
}
SpringDataJPA查询方法
Spring Data Common的Repository
Repository(包括子接口)位于Spring Data Common的lib里面,是Spring Data 里面做数据库操作的最底层的抽象接口,最顶级的父接口,源码中什么方法都没有,仅仅起到一个标示作用。
Repository源码如下:
package org.springframework.data.repository;
import org.springframework.stereotype.Indexed;
@Indexed
public interface Repository<T, ID> {
}
CrudRepository方法详解
CrudRepository源码:
@NoRepositoryBean
public interface CrudRepository<T, ID> extends Repository<T, ID> {
<S extends T> S save(S var1);
<S extends T> Iterable<S> saveAll(Iterable<S> var1);
Optional<T> findById(ID var1);
boolean existsById(ID var1);
Iterable<T> findAll();
Iterable<T> findAllById(Iterable<ID> var1);
long count();
void deleteById(ID var1);
void delete(T var1);
void deleteAll(Iterable<? extends T> var1);
void deleteAll();
}
CrudRepository 主要的功能是单表的增删改查操作。
编写一个自定义都接口UserCrudRepository 去继承CrudRepository
public interface UserCrudRepository extends CrudRepository<User,Long> {
public List<User> findUserByUsername(String str);
}
测试类
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserCrudRepositoryTest {
@Autowired
UserCrudRepository userCrudRepository;
@Test
public void findAll(){
Iterable<User> users = userCrudRepository.findAll();
System.out.println(users);
}
@Test
public void findById(){
Optional<User> optional = userCrudRepository.findById(28L);
User user = optional.get();
System.out.println(user);
}
@Test
public void save(){
User user= new User();
user.setUsername("laogao");
user.setPhone("13611064315");
user.setPassword("123456");
user.setSalt("123");
user.setCreated(new Date());
User user1 = userCrudRepository.save(user);
System.out.println(user1);
}
@Test
public void update(){
User user= new User();
user.setId(33L);
user.setUsername("wangwu");
user.setPhone("13611064316");
user.setPassword("123456");
user.setSalt("123");
user.setCreated(new Date());
User user1 = userCrudRepository.save(user);
System.out.println(user1);
}
@Test
public void delete(){
User user= new User();
user.setId(33L);
userCrudRepository.delete(user);
}
@Test
public void findUserByUsername() {
List<User> users = userCrudRepository.findUserByUsername("jianganming");
System.out.println(users);
}
}
PagingAndSortingRepository方法详解
PagingAndSortingRepository在CrudRepository 中扩展了分页和排序功能
PagingAndSortingRepository源码如下
@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> {
Iterable<T> findAll(Sort var1);
Page<T> findAll(Pageable var1);
}
编写一个自定义都接口UserPagingAndSortingRepository 去继承PagingAndSortingRepository
public interface UserPagingAndSortingRepository
extends PagingAndSortingRepository<User,Long> {
}
测试类
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserPagingAndSortingRepositoryTest {
@Autowired
UserPagingAndSortingRepository userCrudRepository;
//分页
@Test
public void findAll(){
Pageable pageable = PageRequest.of(0, 2);
Page<User> page = userCrudRepository.findAll(pageable);
System.out.println("总页数:"+page.getTotalPages());
System.out.println("总条数:"+page.getTotalElements());
System.out.println("数据:"+page.getContent());
}
//排序
@Test
public void findAllSort(){
Iterable<User> users = userCrudRepository.findAll(
new Sort(Sort.Direction.DESC, "username"));
System.out.println(users);
}
//分页排序一起使用
@Test
public void findAllPageAndSort(){
Sort sort = new Sort(Sort.Direction.DESC, "username");
Pageable pageable = PageRequest.of(0, 2,sort);
Page<User> page = userCrudRepository.findAll(pageable);
System.out.println("总页数:"+page.getTotalPages());
System.out.println("总条数:"+page.getTotalElements());
System.out.println("数据:"+page.getContent());
}
}
定义查询方法
- 定义查询方法的配置
由于Spring JPA Repository的实现原理是采用动态代理机制,我们可以自己定义查询方法,但是方法名称指定特定的查询,或者通过使用@Query手动定义查询,这取决于实际操作。只需要自定义的Repository继承类Spring Data Common里的Repository接口即可。
如果想有其他更多默认通用方法的实现,可以选择CrudRepository,PagingAndSortingRepository,JpaRepository等接口。
如果希望对所暴露对方法有选择性,可以自定义Repository。代码如下
@NoRepositoryBean
public interface BaseRepository<T, ID> extends Repository<T, ID> {
T getOne(ID id);
T save(T entity);
}
interface MyRepository extends BaseRepository<User,Long>{
User findByUsernameAndPassword(String username,String password);
}
- 自定义查询方法创建与关键字
spring data jpa 查询方法有多种,其中最快捷和方便的是方法名查询,命名规则如下
关键字 | 例子 | JPQL语句 |
---|---|---|
AND | findByLastnameAndFirstname | where x.lastname = ?1 and x.firstname = ?2 |
Or | findByLastnameOrFirstname | where x.lastname = ?1 or x.firstname = ?2 |
Between | findByStartDateBetween | where x.startDate between ?1 and ?2 |
LessThan | findByAgeLessThan | where x.age < ?1 |
GreaterThan | findByAgeGreaterThan | where x.age > ?1 |
After | findByStartDateAfter | where x.startDate > ?1 |
Before | findByStartDateBefore | where x.startDate < ?1 |
IsNull | findByAgeIsNull | where x.age is null |
NotNull | findByAgeNotNull | 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 |
查询条件和分页一起使用
Page<User> findUserByPhone(String phone, Pageable pageable);
测试代码
@Test
public void findUserByPhone(){
Pageable pageable = PageRequest.of(0, 2);
Page<User> page = userRepository.findUserByPhone("13600527634",pageable);
System.out.println("总页数:"+page.getTotalPages());
System.out.println("总条数:"+page.getTotalElements());
System.out.println("数据:"+page.getContent());
}