spring data jpa 入门使用笔记

一、JPA的一些面试常识问题:

1. JPA与Hibernate关系


   JPA是持久化规范,而Hibernate是其实现

2. JPA与JDBC的区别


(1)JPA开发效率高,运行效率低
(2)JDBC开发效率低,运行效率高(更接近底层,代码繁琐)
(3)JPA兼容各种数据库(方便移植)
(4)JPA有内置缓存(性能在一定程度上有所优化)
(5)JPA直接面向持久对象操作
(6)JPA不能干涉SQL的生成

二、spring boot + JPA的简单项目

1.导入pom依赖

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

2.实体类POJO配置@Entity、@Table、@Id、@GeneratedValue

@Entity //指定持久化实体类
@Table(name = "t_employee") //指定表名
public class Employee {
     /**
      * @Id  表示主键
      * @GeneratedValue  表示主键自动递增
      */
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private Integer age;
    private String name;
    private Integer height;

    @Transient
    private List<String> menu;
    
    @OneToOne(cascade = CascadeType.REFRESH) // 一对一映射
    private SysDicPO sysDic;  //系统编码  

}

2.2.实体类中特殊注解,及用法:@GeneratedValue 主键自增 , @Transient不储存在数据库,

2.2.1. @GeneratedValue(strategy = GenerationType.IDENTITY)

在这里插入图片描述

2.2.2. @OneToOne 一对一、 @ManyToMany 多对多、 @OneToMany 一对多、@OneToOne(cascade = CascadeType.REFRESH) // 一对一映射

2.2.3. CascadeType的各级权限CascadeType.REFRESH
* CascadeType.REMOVE
Cascade remove operation,级联删除操作。
删除当前实体时,与它有映射关系的实体也会跟着被删除。
* CascadeType.MERGE
Cascade merge operation,级联更新(合并)操作。
当Student中的数据改变,会相应地更新Course中的数据。
* CascadeType.DETACH
Cascade detach operation,级联脱管/游离操作。
如果你要删除一个实体,但是它有外键无法删除,你就需要这个级联权限了。它会撤销所有相关的外键关联。
* CascadeType.REFRESH
Cascade refresh operation,级联刷新操作。
假设场景 有一个订单,订单里面关联了许多商品,这个订单可以被很多人操作,那么这个时候A对此订单和关联的商品进行了修改,与此同时,B也进行了相同的操作,但是B先一步比A保存了数据,那么当A保存数据的时候,就需要先刷新订单信息及关联的商品信息后,再将订单及商品保存。(来自良心会痛的评论)
* CascadeType.ALL
Cascade all operations,清晰明确,拥有以上所有级联操作权限。

链接:https://www.jianshu.com/p/e8caafce5445

2.2.4. fetch = FetchType.LAZY懒加载,FetchType.EAGER急加载
如果是EAGER,那么表示取出这条数据时,它关联的数据也同时取出放入内存中
如果是LAZY,那么取出这条数据时,它关联的数据并不取出来,在同一个session中,什么时候要用,就什么时候取(再次访问数据库)。
但是,在session外,就不能再取了。用EAGER时,因为在内存里,所以在session外也可以取。
一般只在一边设EagerJPA接口默认为一对多为Lazy,多对一为Eager,但是Hibernate反向工程生成Entity时,多对一为Lazy,需要手动改为Eager。
而两边都设Eager,那么代码中取一条记录时,会发2SQL。

链接:https://blog.csdn.net/tongyang8820/article/details/71157948

2.2.5. @JoinTable和@JoinColumn和joinColumns和inverseJoinColumns的用法

   JoinTable是中间表表名 joinColumns指定中间表中关联自己ID的字段,inverseJoinColumns表示中间表中关联对方ID的字段,joinColumn是列名.

@JoinTable(name = "employee_role",joinColumns = @JoinColumn(name = "employee_id"),
inverseJoinColumns = @JoinColumn(name = "role_id"))

//用法2-referencedColumnName自己主表的字段,name=子表的对应字段,orphanRemoval = true:对应是否联级删除
@OneToMany(fetch = FetchType.LAZY,cascade = CascadeType.ALL,orphanRemoval = true)
@JoinColumn(name="buildingRiskId",referencedColumnName="id",insertable=true,updatable=true)
private Set<LocaleRiskPO> localeRiskPOSet;

//-------------------------------------------------------------------------------------------------
public class UserInfo{
    @OneToMany(fetch = FetchType.LAZY,cascade = CascadeType.ALL)
    @JoinTable(name = "tbl_ouser_nuser",
        joinColumns={@JoinColumn(name="nid",referencedColumnName="id")},
        inverseJoinColumns={@JoinColumn(name="oid",referencedColumnName="id")})
    private Set<OldAccountPO> oldAccountSet;
}

2.2.6. @OneToMany,orphanRemoval = true:对应是否联级删除
@OneToMany(fetch = FetchType.LAZY,cascade = CascadeType.ALL,orphanRemoval = true)
@JoinColumn(name="buildingRiskId",referencedColumnName="id",insertable=true,updatable=true)
private Set<LocaleRiskPO> localeRiskPOSet;

3.application.properties文件中添加配置

#数据源为mysql
spring.jpa.database = MYSQL
# Show or not log for each sql query
#控制台打印
spring.jpa.show-sql = true
#update表示需要实体类时会更新
# Hibernate ddl auto (create, create-drop, update)
spring.jpa.hibernate.ddl-auto = update
# Naming strategy
# stripped before adding them to the entity manager)
#JPA方言
#spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
#命名策略
spring.jpa.hibernate.naming.physical-strategy=org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
spring.jpa.hibernate.naming-strategy=org.hibernate.cfg.DefaultNamingStrategy

4.dao层继承JPA核心接口

4.1.SpringBoot JPA提供的核心接口

  • Repository接口
  • CrudRepository接口
  • PagingAndSortingRepository接口
  • JpaRepository接口
  • JPASpecificationExecutor接口
package com.deceen.dao;

import com.deceen.entity.DemoEntity;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;

import java.util.List;


public interface DemoMapper extends JpaRepository<DemoEntity,Integer> {


    List<DemoEntity> findByAgeAndHeight(Integer age,Integer height);

    List<DemoEntity> findByNameLike(String name);

    @Query("from DemoEntity where name = ?1")
    List<DemoEntity> queryByNameUseHQL(String name);

    @Query(value = "select * from demo_entity where name=?",nativeQuery = true)
    List<DemoEntity> queryByNameUseSQL(String name);

    @Query(value = "update demo_entity set name=? where id=?",nativeQuery = true)
    @Modifying  //需要执行一个更新操作
    void updateUsersNameById(String name,Integer id);

}

5.基本操作

5.1. 注入EntityManager entityManager;可以实现自定义sql查询

  @Autowired
    EntityManager entityManager;


    //JPA测试entityManager.createNativeQuery
    //测试 query 返回实体类 entity
    //测试  mapToInt   计算对象和
    @Test
    void test01(){
        String sql="select * from demo_entity where id<:size";
        Query q = entityManager.createNativeQuery(sql);
        q.setParameter("size",30);
        //设置成map
        q.unwrap(SQLQuery.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
        q.unwrap(SQLQuery.class).setResultTransformer(Transformers.aliasToBean(DemoEntity.class));

        List l = q.getResultList();
        System.out.println(l);

        int sum = l.stream().mapToInt(m -> ((DemoEntity) m).getHeight()).sum();
        System.out.println("所有高度和:"+ sum);
    }

5.2. 排序查询

@Test
    void find() {

//        //Order  定义了排序规则
//        Sort.Order order=new Sort.Order(Sort.Direction.DESC,"id");
        //Sort对象封装了排序规则
//        Sort sort=Sort.by(order);
        
		Sort sort1=Sort.by(Sort.Direction.DESC,"height");
        List<DemoEntity> xiao= demoMapper.findAll(sort1);
        xiao.stream().forEach(System.out::println);
    }

5.3. 分页查询

@Test   
void find2() {
  // 分页Pageable       
   DemoEntity entity = new DemoEntity();       
   entity.setAge(15);        
   entity.setHeight(20);        
   //sort条件        
    Example<DemoEntity> sort= Example.of(entity);       
   //pageable 分页包装类        
   Pageable pageable =PageRequest.of(0,2,Sort.Direction.DESC,"id");       
   Page<DemoEntity> page= demoMapper.findAll(sort,pageable);        
   long total = page.getTotalElements();        
   int totalPages = page.getTotalPages();       
   List<DemoEntity> xiao = page.getContent();        
   System.out.println("总记录数:"+total);       
   System.out.println("总页数:"+totalPages);        
   xiao.stream().forEach(System.out::println);    
}

6.JPA修改局部字段

解决办法
通常有两种方法解决此问题:
1、通过传入对象的id,从数据库中查询得到原始对象,然后将要修改的字段封装到原始对象中。再以封装后的对象为参数进行save()。
代码如下:
public FrontResult update(Evaluation evaluation) {
        // 从数据库中获取对象
        Evaluation original = evaluationRepo().findById(evaluation.getId());
        // 复制想要更改的字段值
        BeanUtils.copyProperties(evaluation, original, getNullPropertyNames(evaluation));
        // 更新操作
        evaluationRepo().save(original);
        return FrontResult.init(FrontResult.SUCCEED, "更新成功");
    }

使用的工具类如下(用于获取未被修改的字段名):
public class UpdateUtil {
    public static String[] getNullPropertyNames(Object source) {
        final BeanWrapper src = new BeanWrapperImpl(source);
        java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors();
        Set<String> emptyNames = new HashSet<>();
        for (java.beans.PropertyDescriptor pd : pds) {
            Object srcValue = src.getPropertyValue(pd.getName());
            if (srcValue == null) {
                emptyNames.add(pd.getName());
            }
        }
        String[] result = new String[emptyNames.size()];
        return emptyNames.toArray(result);
    }
}

2、通过注解@Query自己实现sql语句。
注意:
在执行update或者delete方法时,必须加上注解@Modifying@Transactional。
此处的Test要使用实体的类名,不是数据库中的表名。
代码如下:
@Modifying
@Transactional
@Query("update Test a set " +
       "a.name = CASE WHEN :#{#testAre.name} IS NULL THEN a.name ELSE :#{#testAre.name} END ," +
       "a.age = CASE WHEN :#{#testAre.age} IS NULL THEN a.age ELSE :#{#testAre.age} END ," +
       "a.insertTime = CASE WHEN :#{#testAre.insertTime} IS NULL THEN a.insertTime ELSE :#{#testAre.insertTime} END ," +
       "a.spare =  CASE WHEN :#{#testAre.spare} IS NULL THEN a.spare ELSE :#{#testAre.spare} END " +
       "where a.id = :#{#testAre.id}")
int update(@Param("testAre") TestAre testAre);

一般字段比较多时我会选第一种方式,虽然多查了一次数据库,但是省键盘。
https://www.cnblogs.com/hanstrovsky/p/11867878.html
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值