| 知乎专栏 |
Repository: 仅仅是一个标识,没有任何方法,方便Spring自动扫描识别 CrudRepository: 继承Repository,实现了一组CRUD相关的方法 PagingAndSortingRepository: 继承CrudRepository,实现了一组分页排序相关的方法 JpaRepository: 继承PagingAndSortingRepository,实现一组JPA规范相关的方法
Spring Data JPA 为此提供了一些表达条件查询的关键字:
Keyword Sample JPQL snippet And findByLastnameAndFirstname … where x.lastname = ?1 and x.firstname = ?2 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) 常用如下: And --- 等价于 SQL 中的 and 关键字,比如 findByUsernameAndPassword(String user, Striang pwd) Or --- 等价于 SQL 中的 or 关键字,比如 findByUsernameOrAddress(String user, String addr) Between --- 等价于 SQL 中的 between 关键字,比如 findBySalaryBetween(int max, int min) LessThan --- 等价于 SQL 中的 "<",比如 findBySalaryLessThan(int max) GreaterThan --- 等价于 SQL 中的">",比如 findBySalaryGreaterThan(int min) IsNull --- 等价于 SQL 中的 "is null",比如 findByUsernameIsNull() IsNotNull --- 等价于 SQL 中的 "is not null",比如 findByUsernameIsNotNull() NotNull --- 与 IsNotNull 等价 Like --- 等价于 SQL 中的 "like",比如 findByUsernameLike(String user) NotLike --- 等价于 SQL 中的 "not like",比如 findByUsernameNotLike(String user) OrderBy ---等价于 SQL 中的 "order by",比如 findByUsernameOrderBySalaryAsc(String user) Not --- 等价于 SQL 中的 "! =",比如 findByUsernameNot(String user) In --- 等价于 SQL 中的 "in",比如 findByUsernameIn(Collection<String> userList) ,方法的参数可以是 Collection 类型,也可以是数组或者不定长参数 NotIn --- 等价于 SQL 中的 "not in",比如 findByUsernameNotIn(Collection<String> userList) ,方法的参数可以是 Collection 类型,也可以是数组或者不定长
Modifier and Type Method and Description void deleteAllInBatch() Deletes all entities in a batch call. void deleteInBatch(Iterable<T> entities) Deletes the given entities in a batch which means it will create a single Query. List<T> findAll() <S extends T> List<S> findAll(Example<S> example) <S extends T> List<S> findAll(Example<S> example, Sort sort) List<T> findAll(Sort sort) List<T> findAllById(Iterable<ID> ids) void flush() Flushes all pending changes to the database. T getOne(ID id) Returns a reference to the entity with the given identifier. <S extends T> List<S> saveAll(Iterable<S> entities) <S extends T> S saveAndFlush(S entity) Saves an entity and flushes changes instantly.
CrudRepository 接口提供了最基本的对实体类的添删改查操作
T save(T entity); //保存单个实体 Iterable<T> save(Iterable<? extends T> entities);//保存集合 T findOne(ID id); //根据id查找实体 boolean exists(ID id); //根据id判断实体是否存在 Iterable<T> findAll(); //查询所有实体,不用或慎用! long count(); //查询实体数量 void delete(ID id); //根据Id删除实体 void delete(T entity); //删除一个实体 void delete(Iterable<? extends T> entities); //删除一个实体的集合 void deleteAll(); //删除所有实体,不用或慎用!
接口实现 PagingAndSortingRepository
package api.repository.h5;
import org.springframework.data.repository.PagingAndSortingRepository;
import api.domain.User;
public interface GatherRepository extends PagingAndSortingRepository<User, Integer> {
}
控制器添加 Pageable pageable 参数
@RequestMapping("/browse")
public ModelAndView browse(Pageable pageable) {
Page<User> users = userRepository.findAll(pageable);
System.out.println(users.toString());
ModelAndView mv = new ModelAndView();
mv.addObject("users", users.getContent());
mv.addObject("number", users.getNumber());
mv.addObject("size", users.getSize());
mv.addObject("totalPages", users.getTotalPages());
mv.setViewName("table");
return mv;
}
如果 Repository 继承了 PagingAndSortingRepository 你会发 CrudRepository 中的 save 等方法不能使用了,我的解决方法是写两个 Repository
一个 CURD 的 ChatRepository 放在 cn.netkiller.repository
package cn.netkiller.repository;
import cn.netkiller.domain.Chat;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface ChatRepository extends CrudRepository<Chat, String> {
List<Chat> findAllBySession(String session);
Chat findOneBySession(String session);
}
另一个分页的 PagingAndSortingRepository 放在 cn.netkiller.repository.pageable
package cn.netkiller.repository.pageable;
import cn.netkiller.domain.Chat;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface ChatPageableRepository extends PagingAndSortingRepository<Chat, String> {
Page<Chat> findAllByDevice(String device, Pageable pageable);
}
@RequestMapping(value = "/list", method=RequestMethod.GET)
public Page<Blog> getEntryByPageable1(@PageableDefault( sort = { "id" }, direction = Sort.Direction.DESC)
Pageable pageable) {
return blogRepository.findAll(pageable);
}
@RequestMapping(value = "/blog", method=RequestMethod.GET)
public Page<Blog> getEntryByPageable(@PageableDefault(value = 15, sort = { "id" }, direction = Sort.Direction.DESC)
Pageable pageable) {
return blogRepository.findAll(pageable);
}
@RequestMapping(value = "/list", method=RequestMethod.GET)
public Page<Blog> getEntryByPageable2(@PageableDefault Pageable pageable) {
return blogRepository.findAll(pageable);
}
@ModelAttribute("users")
public Page<User> users(@PageableDefault(size = 5) Pageable pageable) {
return userManagement.findAll(pageable);
}
我们只需要在方法的参数中直接定义一个pageable类型的参数,当Spring发现这个参数时,Spring会自动的根据request的参数来组装该pageable对象,Spring支持的request参数如下:
page,第几页,从0开始,默认为第0页
size,每一页的大小,默认为20
sort,排序相关的信息,以property,property(,ASC|DESC)的方式组织,例如sort=firstname&sort=lastname,desc表示在按firstname正序排列基础上按lastname倒序排列
这样,我们就可以通过url的参数来进行多样化、个性化的查询,而不需要为每一种情况来写不同的方法了。
通过url来定制pageable很方便,但唯一的缺点是不太美观,因此我们需要为pageable设置一个默认配置,这样很多情况下我们都能够通过一个简洁的url来获取信息了。
Spring提供了@PageableDefault帮助我们个性化的设置pageable的默认配置。例如@PageableDefault(value = 15, sort = { "id" }, direction = Sort.Direction.DESC)表示默认情况下我们按照id倒序排列,每一页的大小为15。
@Autowired
private ArticleRepository articleRepository;
@RequestMapping("/mysql")
@ResponseBody
public String mysql() {
articleRepository.save(new Article("Neo", "Chen"));
for (Article article : articleRepository.findAll()) {
System.out.println(article);
}
Article tmp = articleRepository.findByTitle("Neo");
return tmp.getTitle();
}
@RequestMapping("/search")
@ResponseBody
public String search() {
for (Article article : articleRepository.findBySearch(1)) { System.out.println(article); }
List<Article> tmp = articleRepository.findBySearch(1L);
tmp.forEach((temp) -> {
System.out.println(temp.toString());
});
return tmp.get(0).getTitle();
}
package cn.netkiller.wallet.repository.fcoin;
import java.util.List;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.CrudRepository;
import cn.netkiller.wallet.domain.fcoin.Fcoin;;
public interface FcoinRepository extends CrudRepository<Fcoin, String> {
Fcoin findOneByAddress(String address);
int countByAirdropFalse();
List<Fcoin> findByAirdrop(boolean airdrop, Pageable pageable);
}
package cn.netkiller.api.repository;
import org.springframework.data.repository.CrudRepository;
import cn.netkiller.api.domain.StatisticsHistory;
public interface StatisticsHistoryRepostitory extends CrudRepository<StatisticsHistory, Long> {
public StatisticsHistory findByMemberIdAndStatisticsIdAndType(long member_id, long statistics_id,
StatisticsHistory.StatisticsType type);
}
@Autowired private StatisticsHistoryRepostitory statisticsHistoryRepostitory; statisticsHistoryRepostitory.findByMemberIdAndStatisticsIdAndType(uid, id, type);
public interface UserRepository extends CrudRepository<User, Long> {
Long countByFirstName(String firstName);
}
deleteBy
@Transactional
Long deleteByFirstName(String firstName);
List<Fruit> deleteByColor(String color);
removeBy
@Transactional
List<User> removeByFirstName(String firstName);
Iterable<PicturePsychoanalysis> findByAnalysisIsNull();
使用 And / Or 链接多个条件
Iterable<Chat> findByAnswerIsNullOrAudioIsNullOrPsychoanalysisIsNull();
IsNotNull
Page<Picture> findByImageIsNotNullAndStoryIsNotNullAndAudioIsNotNull(Pageable pageable);
package cn.netkiller.repository;
import cn.netkiller.domain.Lora;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
import java.util.Set;
@Repository
public interface LoraRepository extends CrudRepository<Lora, Integer> {
Optional<Lora> findByKeywordIn(Set<String> keyword);
Iterable<Lora> findAllByKeywordIn(Set<String> keyword);
}
查询接口
@GetMapping("one")
public Optional<Lora> keywordTest(@RequestParam("key") Set<String> keyword) {
Optional<Lora> lora = loraRepository.findByKeywordIn(keyword);
return lora;
}
@GetMapping("all")
public Iterable<Lora> keywordTest1(@RequestParam("key") Set<String> keyword) {
Iterable<Lora> lora = loraRepository.findAllByKeywordIn(keyword);
return lora;
}
Page<Picture> findByShareTrueAndImageIsNotNullAndStoryIsNotNullAndAudioIsNotNull(Pageable pageable);
// DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime now = LocalDateTime.now();
// LocalDateTime lastDay = now.minusDays(1);
LocalDateTime start = now.minusDays(1);
LocalDateTime end = now.minusMinutes(5);
log.info("Query picture from {} to {}", start, end);
// iterable = pictureService.findBySrtIsNullAndCtimeAfter(end);
iterable = pictureService.findBySrtIsNullAndCtimeBefore(end);
iterable.forEach(picture -> {
String appId = businessService.queryAppIdByDevice(picture.getDevice());
aigcPipeline.setAigc(Aigc.builder().appId(appId).device(picture.getDevice()).session(picture.getSession()).prompt(picture.getPrompt()).story(picture.getStory()).audio(picture.getAudio()).build());
aigcPipeline.subtitles(srt -> {
pictureService.updateSrt(aigcPipeline.getAigc().getSession(), srt);
sessionStatusService.progress(aigcPipeline.getAigc().getSession(), "识别字幕", srt);
});
});
public Iterable<Picture> findBySrtIsNullAndCtimeBefore(LocalDateTime lastDay) {
Iterable<Picture> iterable = pictureRepository.findBySrtIsNullAndCtimeBefore(lastDay);
return iterable;
}
package cn.netkiller.repository;
import cn.netkiller.domain.story.Picture;
import cn.netkiller.repository.model.PictureGroupList;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.time.LocalDateTime;
import java.util.Optional;
@Repository
public interface PictureRepository extends CrudRepository<Picture, Long> {
Picture findAllBySession(String session);
Optional<Picture> findBySession(String session);
Page<Picture> findAll(Pageable pageable);
Iterable<Picture> findBySrtIsNullAndCtimeBefore(LocalDateTime lastDay);
}
LocalDateTime beforeTime = LocalDateTime.now().minusMinutes(2);
Iterable<Assets> iterable = assetsRepository.findByPlanIsNullOrPptxIsNullOrMinutesIsNullOrAudioIsNullAndStatusAndCtimeBefore("Processing", beforeTime);
Iterable<Assets> iterable = assetsRepository.findByStatusAndCtimeBetween("Processing", afterTime, beforeTime);
boolean exists = repository.existsById(searchId)
SQL EXISTS 函数用法
@Query(value = "SELECT EXISTS(SELECT id FROM Member WHERE mobile = :mobile AND expired > CURRENT_TIMESTAMP) AS bool")
Boolean queryExpiredByMobile(@Param("mobile") String mobile);
public List<StudentEntity> findAllByOrderByIdAsc(); public List<StudentEntity> findAllByOrderByIdDesc(); List<RecentRead> findByMemberIdOrderByIdDesc(int memberId, Pageable pageable);
package schedule.repository;
import java.util.Date;
import org.springframework.data.repository.CrudRepository;
import common.domain.CmsTrash;
public interface CmsTrashRepository extends CrudRepository<CmsTrash, Integer> {
Iterable<CmsTrash> findBySiteIdAndTypeOrderByCtimeASC(int siteId, String string);
Iterable<CmsTrash> findBySiteIdAndTypeAndCtimeGreaterThanOrderByCtimeASC(int siteId, String string, Date date);
}
复杂的 GreaterThan / LessThan 应用
@Cacheable(value = "picture:share:next", key = "#cursor", unless = "#result == null")
public Optional<Picture> shareNext(int cursor) {
return pictureRepository.findFirstByShareTrueAndImageIsNotNullAndStoryIsNotNullAndAudioIsNotNullAndIdGreaterThan(cursor);
}
@Cacheable(value = "picture:share:previous", key = "#cursor", unless = "#result == null")
public Optional<Picture> sharePrevious(int cursor) {
return pictureRepository.findFirstByShareTrueAndImageIsNotNullAndStoryIsNotNullAndAudioIsNotNullAndIdLessThanOrderByIdDesc(cursor);
}
List<UserModel> findByName(String name, Sort sort);
Sort sort = new Sort(Direction.DESC, "id");
repostitory.findByName("Neo", sort);
userRepository.findAll(Sort.by(Sort.Direction.ASC, "name"));
userRepository.findAll(Sort.by("LENGTH(name)"));
userRepository.findAll(Sort.by(Sort.Direction.ASC, "name"));
userRepository.findAll(Sort.by("LENGTH(name)"));
Page 返回数据和页码等数据
PageRequest(int page, int size, Sort sort) Deprecated. use PageRequest.of(int, int, Sort) instead.
package cn.netkiller.repository;
import cn.netkiller.domain.Picture;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
@Repository
public interface PictureRepository extends JpaRepository<Picture, Long> {
Picture findAllBySession(String session);
Optional<Picture> findOneBySession(String session);
Page<Picture> findAll(Pageable pageable);
}
public Page<Picture> page(Pageable pageable) {
return pictureRepository.findAll(pageable);
}
@GetMapping("/{device}/page")
public Mono<Page<Picture>> page(@PathVariable String device, Pageable pageable) {
return Mono.just(pictureService.page(pageable));
}
排序 /picture/test/page?sort=id,desc 每页返回数量 /picture/test/page?size=10 返回第二页5条数据 /picture/test/page?size=5&page=1 返回第二页5条数据,ID倒序排序 /picture/test/page?size=5&page=1&sort=id,desc
curl -X 'GET' \ 'http://localhost:8080/picture/test/page?page=0&size=1&sort=id' \ -H 'accept: */*'
package cn.netkiller.api.repository;
import java.util.List;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.CrudRepository;
import cn.netkiller.api.domain.RecentRead;
public interface RecentReadRepostitory extends CrudRepository<RecentRead, Long> {
List<RecentRead> findByMemberId(long id, Pageable pageable);
}
Top 10 实例
@RequestMapping("/recent/read/list/{id}")
public List<RecentRead> recentList(@PathVariable long id) {
int page = 0;
int limit = 10;
List<RecentRead> recentRead = recentReadRepostitory.findByMemberId(id, new PageRequest(page, limit));
return recentRead;
}
翻页返回数据可以选择 Iterable/List 或者 Page。
Iterable/List 只返回数据,不含页码等数据
注意 PageRequest(int page, int size) 在新版 Spring boot 2.x 中已经废弃请使用 PageRequest.of(page, size) 替代
List<Fcoin> fcoins = fcoinRepository.findByAirdrop(false, PageRequest.of(0, size));
public interface MemberRepository extends JpaRepository<User, Integer>, UserRepository {
...
}
我们并不希望每次返回所有字段,JPA默认查询等价 “SELECT * FROM TABLE”
package cn.netkiller.repository.model;
public interface PictureGroupList {
Integer getId();
String getImage();
String getThumbnail();
String getAudio();
String getStory();
}
public Iterable<PictureGroupList> findByPictureGroupId(int pictureGroupId) {
return pictureRepository.findByPictureGroupId(pictureGroupId);
}
package cn.netkiller.domain;
import com.fasterxml.jackson.annotation.JsonIgnore;
import jakarta.persistence.*;
import lombok.Data;
import org.hibernate.annotations.Comment;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;
import java.io.Serializable;
@Entity
@Table(uniqueConstraints = {@UniqueConstraint(name = "UK_device_hobby", columnNames = {"device_id", "hobby"})})
@DynamicUpdate
@DynamicInsert
@Data
@Comment("用户画像")
public class UserPersona implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", unique = true, nullable = false, insertable = false, updatable = false, columnDefinition = "int unsigned")
@Comment("主键")
private Integer id;
@ManyToOne
@Comment("设备")
@JoinColumn(name = "device_id", unique = false, nullable = false, insertable = true, updatable = false)
@JsonIgnore
private Device device;
@Comment("兴趣爱好")
private String hobby;
@Comment("权重")
private Integer weight;
public interface UserPersonaNativeQuery {
// String getId();
String getHobby();
String getWeight();
}
}
解放方法是,定一个接口 UserPersonaNativeQuery
package cn.netkiller.repository;
import cn.netkiller.domain.UserPersona;
import jakarta.transaction.Transactional;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface UserPersonaRepository extends CrudRepository<UserPersona, Integer> {
@Transactional
@Modifying
@Query(nativeQuery = true, value = "INSERT INTO user_persona (device_id, hobby,weight) VALUES ((SELECT id FROM device where sn=:device) , :hobby, 1) ON DUPLICATE KEY UPDATE weight = weight + 1;")
int updateUserPersona(@Param("device") String device, @Param("hobby") String hobby);
List<UserPersona.UserPersonaNativeQuery> findByDeviceId(Integer id);
}
在仓库中指定该接口
[
{
"weight": "10",
"hobby": "小朋友"
},
{
"weight": "10",
"hobby": "妈妈"
},
{
"weight": "10",
"hobby": "自行车"
},
{
"weight": "5",
"hobby": "校园"
},
{
"weight": "5",
"hobby": "欢声笑语"
},
{
"weight": "6",
"hobby": "雪景"
},
{
"weight": "4",
"hobby": "皮筋"
},
{
"weight": "4",
"hobby": "同学们"
},
{
"weight": "4",
"hobby": "爬杆"
},
{
"weight": "4",
"hobby": "女同学"
},
{
"weight": "4",
"hobby": "羽毛球"
},
{
"weight": "4",
"hobby": "歌曲"
},
{
"weight": "1",
"hobby": "冰雪"
},
{
"weight": "1",
"hobby": "包饺子"
},
{
"weight": "1",
"hobby": "哈尔滨"
},
{
"weight": "1",
"hobby": "冻梨"
},
{
"weight": "1",
"hobby": "冰雕"
}
]
@ManyToOne 字段的巧妙处理,正常情况 @ManyToOne 会返回一个对象,如果我们返回List列表,这个对象就会重复出现,首先使用 @JsonIgnore 将其屏蔽,然后创建两个方法 private void setDeviceId(Integer deviceId) 和 private Integer getDeviceId()
package cn.netkiller.domain;
import com.fasterxml.jackson.annotation.JsonIgnore;
import jakarta.persistence.*;
import lombok.Data;
import org.hibernate.annotations.Comment;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;
import java.io.Serializable;
@Entity
@Table(uniqueConstraints = {@UniqueConstraint(name = "UK_device_hobby", columnNames = {"device_id", "hobby"})})
@DynamicUpdate
@DynamicInsert
@Data
@Comment("用户画像")
public class UserPersona implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", unique = true, nullable = false, insertable = false, updatable = false, columnDefinition = "int unsigned")
@Comment("主键")
private Integer id;
@ManyToOne
@Comment("设备")
@JoinColumn(name = "device_id", unique = false, nullable = false, insertable = true, updatable = false)
@JsonIgnore
private Device device;
@Comment("兴趣爱好")
private String hobby;
@Comment("权重")
private Integer weight;
private void setDeviceId(Integer deviceId) {
this.setDevice(Device.builder().id(deviceId).build());
}
private Integer getDeviceId() {
return this.getDevice().getId();
}
public interface UserPersonaNativeQuery {
String getId();
String getHobby();
Integer getWeight();
Integer getDeviceId();
}
}