spring data jpa 学习(一)

spring data jpa

spring data是spring的一个子项目,在官网就可以发现,springdata和springboot,springcloud等是一个级别的项目
在这里插入图片描述
springdata主要是做数据层的工作,包含了Spring Data Commons,Spring Data JPA,Spring Data MongoDB,Spring Data Redis等模块,其中Spring Data Commons是公共部分,Spring Data JPA,Spring Data MongoDB,Spring Data Redis等是具体的数据操作实现,Spring Data JPA是对关系型数据库的操作,Spring Data MongoDB是对mongodb的操作,Spring Data Redis是对redis的操作

Spring Data Commons之Repository接口

Repository接口是springdate操作db的最顶级接口,ctrl+h调出层级关系
在这里插入图片描述
ReactiveCrudRepository:响应式编程,主要支持当前 NoSQL 方面的操作

RxJava2CrudRepository:为了支持 RxJava 2 做的标准响应式编程的接口

CoroutineCrudRepository :为了支持 Kotlin 语法而实现的

CrudRepository :主要就是jpa相关的操作接口
在这里插入图片描述
Repository:没有任何方法。

CrudRepository:顾名思义,包含了简单的crud方法

PagingAndSortingRepository:顾名思义,包含了简单的分页排序方法,因为也继承了CrudRepository接口,所以也有简单的crud方法

QueryByExampleExecutor:顾名思义,包含了简单的Example查询方法

JpaRepository:jpa的拓展方法,拥有PagingAndSortingRepository接口和QueryByExampleExecutor接口拥有的方法

SimpleJpaRepository:jpa接口的默认实现类

Spring Data JPA是利用方法名来进行crud操作的

@Entity
@Data
public class Users {
	@Id
	@GeneratedValue(strategy= GenerationType.IDENTITY)
	private Long userid;
	private String username;
	private Integer age;
	private String address;
}

@Repository
public interface UserDao extends JpaRepository<Users,Long> {
   Users findUsersByUsername(String username);//通过username查询user
}

语法:查询策略(关键字)+ 查询字段 + 一些限制性条件组成
在这里插入图片描述

在PartTree里面可以发现是通过正则表达式匹配的
在这里插入图片描述
在枚举Type里面发现,限制条件是通过枚举类事先弄好的,不是随意写的

分页排序

分页:Pageable

排序:Sort

Page<Users> findByUsername(String username, Pageable pageable);
Slice<Users> findByAddress(String address, Pageable pageable);
List<Users> findByUsername(String username, Sort sort);
List<Users> findByAge(Integer age, Pageable pageable);

查询实体其中几个字段

定义一个dto

如果只想查询某个实体的其中几个字段,可以直接定义dto的返回类型

@Data
@AllArgsConstructor//全参的构造方法很重要
public class UserDto {
   private String username;
}
@Repository
public interface UserDao extends JpaRepository<Users,Long> {
   UserDto findByUsername(String username);
}

这样就可以直接拿username的数据了,这个只能做查询,不能做更新操作

还可以使用接口定义

public interface UserOnlyUsername {
   String getUsername();
}
@Repository
public interface UserDao extends JpaRepository<Users,Long> {
   UserOnlyUsername findTopByUsername(String username);
}

这样也可以直接获取username数据

@Query用法

@Query使用的是JPQL语法,JPQL类似于SQL语法,不过JPQL操作的是实体对象

@Repository
public interface UserDao extends JpaRepository<Users,Long> {
   /***
    * query查询
    */
   @Query(value = "from Users where username=:username")
   Users findByUsername(@Param("username") String  username);//@Param可以不用,不使用就按个按照顺序对应到JPQL的参数里面,使用了就可以不按照顺序,底层是按照名称查找

}

1、Like查询

@Repository
public interface UserDao extends JpaRepository<Users,Long> {
   @Query(value = "select u from Users u where u.username like concat('%',?1,'%')")
   List<Users> findByUsernameLike(String username);
}

我在最开始写的是@Query(value = "select Users from Users where username like concat('%',?1,'%')")报错了,后面就加了一个别名就好了,我猜测是不是不使用别名的话默认select后面跟的是字段,而没有Users的字段所以启动报错了

2、使用原始sql查询

使用nativeQuery = true,表示使用原始的sql

@Repository
public interface UserDao extends JpaRepository<Users,Long> {
  	@Query(value = "select * from users where username = :username", nativeQuery = true)
	Users findByUsername(@Param("username") String  username);
}

如果使用原始的sql的话,不能直接加加排序

@Repository
public interface UserDao extends JpaRepository<Users,Long> {
    //这是错误的做法
  	@Query(value = "select * from users where username = :username", nativeQuery = true)
	Users findByUsername(@Param("username") String  username,Sort sort);//不能直接在后面加sort排序
}

要把sort当做参数一样传递进去,所以需要加sql里面加上order by

@Repository
public interface UserDao extends JpaRepository<Users,Long> {
    //这是正确的做法
  	@Query(value = "select * from users where username = :username order by :sort", nativeQuery = true)
	Users findByUsername(@Param("username") String  username,String sort);
}

返回指定dto

@Entity
@Data
public class UsersExtend {
   @Id
   @GeneratedValue(strategy= GenerationType.IDENTITY)
   private Long id;
   private Long userid;
   private String card;
   private Double height;
   private Double weight;
}
@Entity
@Data
public class Users {
	@Id
	@GeneratedValue(strategy= GenerationType.IDENTITY)
	private Long userid;
	private String username;
	private Integer age;
	private String address;
}

一张用户表,一张用户信息拓展表,我们现在要返回username,address,card信息

1、直接查询想要的字段

@Repository
public interface UserDao extends JpaRepository<Users,Long> {
   @Query(value = "select u.username,u.address,e.card from Users u ,UsersExtend e where u.userid = e.userid and u.username = :username" )
   List<Object> findUsersByUsername(@Param("username")String username);
}

这样不好的地方在于返回的是List<Object>,而Object肯定不是我们想要的东西,还得拿出来在赋值给新定义的dto,很麻烦

2、使用dto直接赋值

@Data
@AllArgsConstructor
public class UsersDto {
   private String username;
   private String address;
   private String card;
}
@Repository
public interface UserDao extends JpaRepository<Users,Long> {
   @Query(value = "select new com.zhu.jpa.pojo.UsersDto(u.username,u.address,e.card) from Users u ,UsersExtend e where u.userid = e.userid and u.username = :username")
   List<UsersDto> findUsersByUsernameByUsersDto(@Param("username")String username);
}

3、使用接口返回

public interface UsersDtoView {
   String getUsername();
   String getAddress();
   String getCard();
}
@Repository
public interface UserDao extends JpaRepository<Users,Long> {
   @Query(value = "select u.username as username,u.address as address,e.card as card from Users u ,UsersExtend e where u.userid = e.userid and u.username = :username")
	List<UsersDtoView> findUsersByUsernameByUsersDtoView(@Param("username")String username);
}

这样返回的结果是只读的,职责单一,是比较好的

在前面我们使用了一个concat关键字做字符串的拼接,在ParameterizedFunctionExpression中还可以看到其他可以使用的关键字

public static final List<String> STANDARD_JPA_FUNCTION_NAMES = Arrays.asList("CONCAT", "SUBSTRING", "TRIM", "UPPER", "LOWER", "LOCATE", "LENGTH", "ABS", "SQRT", "MOD", "SIZE", "INDEX", "CURRENT_DATE", "CURRENT_TIME", "CURRENT_TIMESTAMP");

@Query动态查询

public interface UsersExtendDao extends JpaRepository<UsersExtend,Long> {
   //使用JPQL
    @Query(value = "select e from UsersExtend e where (:card is null or e.card = :card) and (:weight is null or e.weight = :weight)")
   List<UsersExtend> findByCardAndAndWeight(String card, Double weight);
  //使用原始sql
   @Query(value = "select e.* from users_extend e where (:card is null or e.card = :card) and (:weight is null or e.weight = :weight)",nativeQuery = true)
   List<UsersExtend> findByCardAndWeight(String card, Double weight);
}

如果想查看执行的sql和参数,可以配置日志

#打印sql
spring.jpa.show-sql=true
#打印参数
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=trace

QueryByExampleExecutor

public interface QueryByExampleExecutor<T> {
    <S extends T> Optional<S> findOne(Example<S> var1);

    <S extends T> Iterable<S> findAll(Example<S> var1);

    <S extends T> Iterable<S> findAll(Example<S> var1, Sort var2);

    <S extends T> Page<S> findAll(Example<S> var1, Pageable var2);

    <S extends T> long count(Example<S> var1);

    <S extends T> boolean exists(Example<S> var1);
}

JpaRepository继承了QueryByExampleExecutor接口,所以JpaRepository也拥有了QueryByExampleExecutor的全部方法

public List<Users> findUsersByExample(String username){
    Users probe = new Users();
    probe.setUsername("xiao");
    ExampleMatcher matcher =ExampleMatcher.matchingAll().withMatcher("username",ExampleMatcher.GenericPropertyMatchers.startsWith());
    Example example =Example.of(probe,matcher);
    List<Users> all = userDao.findAll(example);
    return all;
}

JpaSpecificationExecutor

public interface JpaSpecificationExecutor<T> {
    Optional<T> findOne(@Nullable Specification<T> var1);

    List<T> findAll(@Nullable Specification<T> var1);

    Page<T> findAll(@Nullable Specification<T> var1, Pageable var2);

    List<T> findAll(@Nullable Specification<T> var1, Sort var2);

    long count(@Nullable Specification<T> var1);
}

EntityManager

JPA规定,操作数据库实体话必须通过EntityManager 操作,我们知道spring data jpa的实现类是SimpleJpaRepository,而SimpleJpaRepository在底层操作也是EntityManager 实现的,如下,进行删除操作的时候

public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID> {
    private final EntityManager em;
    public SimpleJpaRepository(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager) {
        this.escapeCharacter = EscapeCharacter.DEFAULT;
        Assert.notNull(entityInformation, "JpaEntityInformation must not be null!");
        Assert.notNull(entityManager, "EntityManager must not be null!");
        this.entityInformation = entityInformation;
        this.em = entityManager;
        this.provider = PersistenceProvider.fromEntityManager(entityManager);
    }
    @Transactional
    public void delete(T entity) {
        Assert.notNull(entity, "Entity must not be null!");
        if (!this.entityInformation.isNew(entity)) {
            Class<?> type = ProxyUtils.getUserClass(entity);
            T existing = this.em.find(type, this.entityInformation.getId(entity));
            if (existing != null) {
                this.em.remove(this.em.contains(entity) ? entity : this.em.merge(entity));
            }
        }
    }
}

EntityManager 比较常用的方法

public interface EntityManager {

  //用于将新创建的Entity纳入EntityManager的管理。该方法执行后,传入persist()方法的 Entity 对象转换成持久化状态。
  public void persist(Object entity);

  //将游离态的实体merge到当前的persistence context里面,一般用于更新。
  public <T> T merge(T entity);

  //将实体对象删除,物理删除
  public void remove(Object entity);

  //将当前的persistence context中的实体,同步到数据库里面,只有执行了这个方法,上面的EntityManager的操作才会DB生效;
  public void flush();

  //根据实体类型和主键查询一个实体对象;
  public <T> T find(Class<T> entityClass, Object primaryKey);

  //根据JPQL创建一个Query对象
  public Query createQuery(String qlString);

  //利用CriteriaUpdate创建更新查询
  public Query createQuery(CriteriaUpdate updateQuery);

  //利用原生的sql语句创建查询,可以是查询、更新、删除等sql
  public Query createNativeQuery(String sqlString);
}

如果想自己定义数据库操作可以使用EntityManager ,可以通过@PersistenceContext注解将容器管理的EntityManager注入进来

@PersistenceContext
private EntityManager entityManager;

自定义repository

我们知道JpaRepository里面delete方法是物理删除,如果需要改成逻辑删除那么就需要我们重新定义删除方法

1、定义一个接口CustomizedUserRepository

public interface CustomizedUserRepository {
   void deleteUsers(Users users);
}

实现类

public class CustomizedUserRepositoryImpl implements CustomizedUserRepository{
   private EntityManager entityManager;
   public CustomizedUserRepositoryImpl(EntityManager entityManager) {
      this.entityManager = entityManager;
   }
   @Transactional
   @Override
   public void deleteUsers(Users users) {
      //还没有定义逻辑删除字段,用address代替一下
      users.setAddress("逻辑删除");
      entityManager.merge(users);
      entityManager.flush();
   }
}
//userdao继承定义好的接口
public interface UserDao extends JpaRepository<Users,Long>,CustomizedUserRepository
//在service里面可以直接调用
public void deleteUsers(String username){
   Optional<Users> optional = userDao.findById(1L);
   Users users = optional.get();
   System.out.println(users);
   userDao.deleteUsers(users);
}

在这里插入图片描述

修改成功

2、上面这种方法就是要使用自己定义的deleteUsers方法,现在我们想直接覆盖原来的delete方法,也是可以行的,可以通过@EnableJpaRepositories 指定 repositoryBaseClass

@SpringBootApplication
@EnableJpaRepositories(repositoryBaseClass = CustomizedUsersRepository.class)
public class JpaApplication {

   public static void main(String[] args) {
      SpringApplication.run(JpaApplication.class, args);
   }
}

新建CustomizedUsersRepository类继承SimpleJpaRepository

@Transactional(readOnly = true)
public class CustomizedUsersRepository<T,ID> extends SimpleJpaRepository<T, ID>{
   private final JpaEntityInformation<T, ?> entityInformation;
   private final EntityManager em;
   public CustomizedUsersRepository(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager) {
      super(entityInformation, entityManager);
      this.entityInformation = entityInformation;
      this.em = entityManager;
   }
   public CustomizedUsersRepository(Class<T> domainClass, EntityManager em) {
      super(domainClass, em);
      entityInformation = null;
      this.em = em;
   }

   @Transactional
   @Override
   public void delete(T entity) {
      //如果是逻辑删除的话,我们的实体应该有一个基类,你面包含了公共的字段,如果是否删除等
      //然后就可以在这里进行修改字段值达到更新效果
      System.out.println(entity);
   }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值