SpringBoot JPA 学习总结

深入理解SpringBoot JPA:从基础到实践
本文详细介绍了Java Persistence API (JPA) 的核心概念,包括ORM映射元数据、JPA API、查询语言以及如何在Spring Boot应用中集成和配置JPA。通过对JPA常用注解、Repository接口的探讨,揭示了JPA简化持久化操作的优势,同时也指出了其在复杂查询上的局限性,为开发者选择合适的持久层技术提供了参考。

1. 什么是JPA ?

全称Java Persistence API ,通俗来讲就是一套API框架,通过操作相应API,完成实体对象持久化存储到数据库的操作。

2 JPA 能做什么?

ORM映射元数据:通过注解或xml 配置方式,建立对象和表关系。(JPA支持XML和注解两种元数据的形式,元数据描述对象和表之间的映射关系,框架据此将实体对象持久化到数据库表中)

JPA 的API:一套执行CRUD 的操作API。封装了不同的方法进行操作。(用来操作实体对象,执行CRUD操作,框架在后台替我们完成所有的事情,开发者从繁琐的JDBC和SQL代码中解脱出来。)

查询语言:查询sql 时,通过对象进行查询,而非原生SQL.(通过面向对象而非面向数据库的查询语言查询数据,避免程序的SQL语句紧密耦合。)

JPA 本身是一套规范,其他厂商基于JPA 规范做了相应实现,比如Hibernate, Spring Data JPA 等。类似于Log4J 规范,向Logback,Slf4j 等具体实现了日志操作。

3. 接口约定关键字

关键字

描述

方法命名

样例

And

sql and 条件

findByUsernameAndPwd

where username = ? and pwd = ?

Or

sql or 条件

findByUserNameOrEmail

where username =? or email=?

Is,Equals

sql = 条件

findById,findByIdEquals

where id = ?

Between

sql between 条件

findByIdBetween

where id between ? and ?

LessThan

sql < 条件

findByIdLessThan

where id < ?

LessThanEquals

sql <= 条件

findByIdLessThanEquals

where id<=?

GreaterThan

sql > 条件

findByIdGreaterThan

where id > ?

GreaterThanEquals

sql >= 条件

findByIdGreaterThanEquals

where id >=?

After

sql > 条件

findByIdAfter

where id >?

Before

sql < 条件

findByIdBefore

where id < ?

IsNull

sql is null

findByIdIsNull

where id is null

NotNull

sql not null

findByIdNotNull

where id is not null

Like

sql like

findByNameLike

where name like ?

NotLike

sql not like

findByNameNotLike

where name not like ?

In

sql in

findByIdIn(Collection c)

where id in (?)

NotIn

sql not in

findByIdNotIn(Collection c)

where id not in (?)

StartingWith

sql like ?%

findByNameStartingWith

where name like '?%'

EndingWith

sql like %?

findByNameEndingWith

where name like '%?'

Containing

sql like %?%

findByNameContaining

where name like '%?%'

4 JPA 集成依赖

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
   <groupId>mysql</groupId>
   <artifactId>mysql-connector-java</artifactId>
</dependency>

5. application 配置

spring.datasource.url=jdbc:mysql://IP:3306/test?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
spring.datasource.username=test
spring.datasource.password=test
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.properties.hibernate.hbm2ddl.auto=create
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true

hbm2ddl.auto 配置需要注意

create:每次加载hibernate时都会删除上一次的生成的表,然后根据你的model类再重新来生成新表,哪怕两次没有任何改变也要这样执行,这就是导致数据库表数据丢失的一个重要原因。
create-drop:每次加载hibernate时根据model类生成表,但是sessionFactory一关闭,表就自动删除。
update:最常用的属性,第一次加载hibernate时根据model类会自动建立起表的结构(前提是先建立好数据库),以后加载hibernate时根据model类自动更新表结构,即使表结构改变了但表中的行仍然存在不会删除以前的行。要注意的是当部署到服务器后,表结构是不会被马上建立起来的,是要等应用第一次运行起来后才会。
validate:每次加载hibernate时,验证创建数据库表结构,只会和数据库中的表进行比较,不会创建新表,但是会插入新值。

6. JPA 的使用

(1)常用注解:

@Entity 实体标注,数据库表和对象映射

@Id 标注一个字段是ID

@GeneratedValue 标注ID生成策略, 默认使用AUTO ,会在数据库中生成一张表,hibernate_sequence 存储ID的下一个值。

public enum GenerationType {
    TABLE,
    SEQUENCE,
    IDENTITY,
    AUTO;

    private GenerationType() {
    }
}

@Column 标注列属性,大部分属性同数据库表字段,长度,是否非空等。

(2) 基本常用接口

创建Repository 接口 继承 JpaRepository<T,ID> ,基本常用操作都可以通过此接口完成。

public interface AddressRepository extends JpaRepository<Address, Long> {
}
@Resource
private AddressRepository addressRepository;
  
public void test(){ 
    Address address=new Address();
    address.setUserId(1L);
    address.setCity("北京");
    address.setProvince("北京");
    address.setStreet("天安门");
    addressRepository.save(address);
}

(3)方法覆盖,SQL 绑定

方法命名规则采用约定规则,SQL 使用对象的方式进行, 参数通过?数字的形式传递,参数内容为方法中的参数值

public interface UserRepository extends JpaRepository<User, Long> {

    User findByUserName(String userName);
    User findByUserNameOrEmail(String username, String email);

    @Transactional(timeout = 10)
    @Modifying
    @Query("update User set userName = ?1 where id = ?2")
    int modifyById(String  userName, Long id);

    @Transactional
    @Modifying
    @Query("delete from User where id = ?1")
    void deleteById(Long id);

    @Query("select u from User u where u.email = ?1")
    User findByEmail(String email);

    @Query("select u from User u")
    Page<User> findALL(Pageable pageable);

    Page<User> findByNickName(String nickName, Pageable pageable);

    Slice<User> findByNickNameAndEmail(String nickName, String email,Pageable pageable);
}
@Resource
private UserRepository userRepository;

public void test(){
    Date date = new Date();
    DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG);
    String formattedDate = dateFormat.format(date);
    User user=new User("ff", "ff123456","ff@126.com", "ff",  formattedDate);
    List<User> u0 = userRepository.findAll();
    Optional<User> u = userRepository.findById(3L);

    String name = u.map(e -> e.getNickName()).get();

    userRepository.save(user);
    user.setId(2L);
    userRepository.delete(user);
    userRepository.count();
    userRepository.estsById(3L);
}

(3) 复杂查询

# 定义接口返回信息

public interface UserInfo {
   String getUserName();
   String getEmail();
   String getHobby();
   String getIntroduction();
}
# 定义多表连接返回内容
public interface UserDetailRepository extends JpaSpecificationExecutor<UserDetail>,JpaRepository<UserDetail, Long>  {

    UserDetail  findByHobby(String hobby);

    @Query("select u.userName as userName, u.email as email, d.introduction as introduction , d.hobby as hobby from User u , UserDetail d " +
            "where u.id=d.userId  and  d.hobby = ?1 ")
    List<UserInfo> findUserInfo(String hobby);
}

@Resource
private UserDetailRepository userDetailRepository;

List<UserInfo> userInfos=userDetailRepository.findUserInfo("钓鱼");
for (UserInfo userInfo:userInfos){
   System.out.println("userInfo: "+userInfo.getUserName()+"-"+userInfo.getEmail()+"-"+userInfo.getHobby()+"-"+userInfo.getIntroduction());
}

(4)条件组合查询

# 定义接口

public interface UserDetailService {
    public Page<UserDetail> findByCondition(UserDetailParam detailParam, Pageable pageable);
}

root 当前返回的对象类引用,可以获取对应参数,detailParam 获取参数对应值, cb 参数条件判断。predicates 组合where 条件。

// 定义实现类

@Service
public class UserDetailServiceImpl implements  UserDetailService{

    @Resource
    private UserDetailRepository userDetailRepository;

    @Override
    public Page<UserDetail> findByCondition(UserDetailParam detailParam, Pageable pageable){

        return userDetailRepository.findAll((root, query, cb) -> {
            List<Predicate> predicates = new ArrayList<Predicate>();
            //equal 示例
            if (!StringUtils.isNullOrEmpty(detailParam.getIntroduction())){
                predicates.add(cb.equal(root.get("introduction"),detailParam.getIntroduction()));
            }
            //like 示例
            if (!StringUtils.isNullOrEmpty(detailParam.getRealName())){
                predicates.add(cb.like(root.get("realName"),"%"+detailParam.getRealName()+"%"));
            }
            //between 示例
            if (detailParam.getMinAge()!=null && detailParam.getMaxAge()!=null) {
                Predicate agePredicate = cb.between(root.get("age"), detailParam.getMinAge(), detailParam.getMaxAge());
                predicates.add(agePredicate);
            }
            //greaterThan 大于等于示例
            if (detailParam.getMinAge()!=null){
                predicates.add(cb.greaterThan(root.get("age"),detailParam.getMinAge()));
            }
            return query.where(predicates.toArray(new Predicate[predicates.size()])).getRestriction();
        }, pageable);

    }
//定义参数类
public class UserDetailParam {
    private String userId;
    private Integer minAge;
    private Integer maxAge;
    private String realName;
    private String introduction;
    private String city;

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public Integer getMinAge() {
        return minAge;
    }

    public void setMinAge(Integer minAge) {
        this.minAge = minAge;
    }

    public Integer getMaxAge() {
        return maxAge;
    }

    public void setMaxAge(Integer maxAge) {
        this.maxAge = maxAge;
    }

    public String getRealName() {
        return realName;
    }

    public void setRealName(String realName) {
        this.realName = realName;
    }

    public String getIntroduction() {
        return introduction;
    }

    public void setIntroduction(String introduction) {
        this.introduction = introduction;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }
}
@Resource
private UserDetailService userDetailService;
@Test
public void testFindByCondition()  {
   int page=0,size=10;
   Sort sort = new Sort(Sort.Direction.DESC, "id");
   Pageable pageable = PageRequest.of(page, size, sort);
   UserDetailParam param=new UserDetailParam();
   param.setIntroduction("程序员");
   param.setMinAge(10);
   param.setMaxAge(30);
   Page<UserDetail> page1=userDetailService.findByCondition(param,pageable);
   for (UserDetail userDetail:page1){
      System.out.println("userDetail: "+userDetail.toString());
   }
}

总结,JPA 在Hibernate基础上更简化的封装常用的SQL CURD操作,大大简化的业务持久化操作,有优点也有缺点,复杂组合SQL 上,使用起来比较麻烦, 可以把基本操作和复杂操作分开,可以提高开发效率,具体还是跟团队使用吻合更好,有人喜欢纯SQL,有人系统Mybatis 框架,按需选择就是最好的选择。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值