SpringBoot系列(6)---SpringBoot-JPA

SpringBoot系列:Spring Boot集成Spring Data JPA 既然选择这个行业,选择了做一个程序员,也就明白只有不断学习,积累实战经验才有资格往上走,拿高薪,为自己,为父母,为以后的家能有一定的经济保障。学习时间都是自己挤出来的,短时间或许很难看到效果,一旦坚持下来了,必然会有所改变。不如好好想想自己为什么想进入这个行业,给自己内心一个答案。面试大厂,最重要的就是夯实的基础,不然面试官随便一问你就凉了;其次会问一些技术原理,还会看你对知识掌握的广度,最重要的还是你的思路,这是面试官比较看重的。 阅读详情

JPA 应该都熟悉了,我就不多说了什么是JPA了。目前JPA主要实现由hibernate和openJPA等。

Spring Data JPA 是Spring Data 的一个子项目,它通过提供基于JPA的Repository极大了减少了操作JPA的代码。笔者觉得这个由SpringBoot 提供的JPARepository真的是非常爽。基本上大部分的业务都可以满足了。

在Spring环境中需要配置大量了XML配置,但是SpringBoot基本上帮助我们配置好了,我们只需要简单地配置一下DataSource和几项jpa额外的配置就完成了整个配置持久化层的工作。EntityManagerFactory 那些都不用配置了。

常规贴出一下Spring-data-JPA的MAVEN

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


一、数据源和JPA配置

当然我们还是需要在application.properties配置数据源,SpringBoot默认是使用tomcat的数据源(intellij有自动提示,spring.datasource.tomcat开头的配置):

#datasource
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_db?charset=utf8mb4&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=tonyyan
spring.datasource.tomcat.max-active=20
spring.datasource.tomcat.test-while-idle=true
spring.datasource.tomcat.validation-query=select 1
spring.datasource.tomcat.default-auto-commit=false
spring.datasource.tomcat.min-idle=15
spring.datasource.tomcat.initial-size=15
最后我们做一些JPA的配置,spring.jpa开头的配置

spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jackson.serialization.indent-output=true
当然SpringBoot的maven也没有添加mysql driver的依赖。所以要吃自己:

<dependency>
   <groupId>mysql</groupId>
   <artifactId>mysql-connector-java</artifactId>
   <version>6.0.6</version>
</dependency>

二、Entity配置

开始之前我先提供一下我的entity配置,便于理解后面的查询操作,我们使用模仿微博的数据实体:

User类:

@Entity
@Table(name = "users")
public class User {

    public User() {
    }

    public User(String username, String userpwd) {
        this.username = username;
        this.userpwd = userpwd;
    }

    private long userId;
    private String username;
    private String userpwd;

    @JsonIgnore
    private Set<Weibo> weibos;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    public long getUserId() {
        return userId;
    }

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

    @Column(name="username")
    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    @Column(name = "userpwd")
    public String getUserpwd() {
        return userpwd;
    }

    public void setUserpwd(String userpwd) {
        this.userpwd = userpwd;
    }

    @OneToMany(fetch = FetchType.LAZY,cascade = {CascadeType.REMOVE},mappedBy = "user")
    public Set<Weibo> getWeibos() {
        return weibos;
    }

    public void setWeibos(Set<Weibo> weibos) {
        this.weibos = weibos;
    }

    @Override
    public String toString() {
        return "User{" +
                "userId=" + userId +
                ", username='" + username + '\'' +
                ", userpwd='" + userpwd + '\'' +
                '}';
    }
}

Weibo类:

@Entity
@Table(name="weibo")
public class Weibo {

    public Weibo() {
    }

    public Weibo(User user, String weiboText, Date createDate) {
        this.user = user;
        this.weiboText = weiboText;
        this.createDate = createDate;
    }

    private long weiboId;
    private User user;
    private String weiboText;
    private Date createDate;
    private Set<Comment> comments;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    public long getWeiboId() {
        return weiboId;
    }

    public void setWeiboId(long weiboId) {
        this.weiboId = weiboId;
    }

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "user_id")
    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    @Column(name = "weibo_text")
    public String getWeiboText() {
        return weiboText;
    }

    public void setWeiboText(String weiboText) {
        this.weiboText = weiboText;
    }

    @Column(name = "create_date")
    public Date getCreateDate() {
        return createDate;
    }

    public void setCreateDate(Date createDate) {
        this.createDate = createDate;
    }

    @OneToMany(fetch = FetchType.LAZY,cascade = {CascadeType.REMOVE},mappedBy = "weibo")
    public Set<Comment> getComments() {
        return comments;
    }

    public void setComments(Set<Comment> comments) {
        this.comments = comments;
    }

    @Override
    public String toString() {
        return "Weibo{" +
                "weiboId=" + weiboId +
                ", user=" + user +
                ", weiboText='" + weiboText + '\'' +
                ", createDate=" + createDate +
                '}';
    }
}

comment评论类:

@Entity
@Table(name = "comments")
public class Comment {

    public Comment() {
    }

    public Comment(User user, Weibo weibo, String commentText, Date commentDate) {
        this.user = user;
        this.weibo = weibo;
        this.commentText = commentText;
        this.commentDate = commentDate;
    }

    private long commentId;
    private User user;
    @JsonIgnore
    private Weibo weibo;
    private String commentText;
    private Date commentDate;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    public long getCommentId() {
        return commentId;
    }

    public void setCommentId(long commentId) {
        this.commentId = commentId;
    }

    @ManyToOne
    @JoinColumn(name = "user_id")
    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "weibo_id")
    public Weibo getWeibo() {
        return weibo;
    }

    public void setWeibo(Weibo weibo) {
        this.weibo = weibo;
    }

    @Column(name = "comment_text")
    public String getCommentText() {
        return commentText;
    }

    public void setCommentText(String commentText) {
        this.commentText = commentText;
    }

    @Column(name = "comment_date")
    public Date getCommentDate() {
        return commentDate;
    }

    public void setCommentDate(Date commentDate) {
        this.commentDate = commentDate;
    }

    @Override
    public String toString() {
        return "Comment{" +
                "commentId=" + commentId +
                ", user=" + user +
                ", weibo=" + weibo +
                ", commentText='" + commentText + '\'' +
                ", commentDate=" + commentDate +
                '}';
    }
}

三、强大的Spring JpaRepository

SpringBoot创建DAO层很多种方法其中japrepository是最强大的而且最有特色的一种,我们可以针对不同的实体创建repository接口。Spring会根据方法名称的规则进行自动生成实现,强大的不要不要的。在SpringBoot中默认已经提供了非常多的常规CRUD操作的repository,以下就是Spring为我们提供的repository接口:

@NoRepositoryBean
public interface JpaRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
    List<T> findAll();

    List<T> findAll(Sort var1);

    List<T> findAll(Iterable<ID> var1);

    <S extends T> List<S> save(Iterable<S> var1);

    void flush();

    <S extends T> S saveAndFlush(S var1);

    void deleteInBatch(Iterable<T> var1);

    void deleteAllInBatch();

    T getOne(ID var1);

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

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

@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {
    Iterable<T> findAll(Sort var1);

    Page<T> findAll(Pageable var1);
}

@NoRepositoryBean
public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> {
    <S extends T> S save(S var1);

    <S extends T> Iterable<S> save(Iterable<S> var1);

    T findOne(ID var1);

    boolean exists(ID var1);

    Iterable<T> findAll();

    Iterable<T> findAll(Iterable<ID> var1);

    long count();

    void delete(ID var1);

    void delete(T var1);

    void delete(Iterable<? extends T> var1);

    void deleteAll();
}

这些操作完全不用我们去实现,这些不是我们以往在普通的Spring项目中自己所定义的BaseDao吗?SpringBoot真的是非常体贴,大大减低了我们的工作量。但是更为强大的还在后面。我们通过继承JpaRepository接口,除了可以获得上面的基础CRUD操作方法之外,还可以通过Spring规定的接口命名方法自动创建复杂的CRUD操作,以下是我在Spring Data JPA 文档中找到的命名规则表:

KeywordSampleJPQL snippet

And

findByLastnameAndFirstname

… where x.lastname = ?1 and x.firstname = ?2

Or

findByLastnameOrFirstname

… where x.lastname = ?1 or x.firstname = ?2

Is,Equals

findByFirstname,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<Age> ages)

… where x.age in ?1

NotIn

findByAgeNotIn(Collection<Age> 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)

以下就是我写的UserRepository接口,后面的WeiboRepository还有更加复杂的操作:

public interface UserRepository extends JpaRepository<User,Long> {

    //查询用户名称包含username字符串的用户对象
    List<User> findByUsernameContaining(String username);

    //获得单个用户对象,根据username和pwd的字段匹配
    User getByUsernameIsAndUserpwdIs(String username,String pwd);

    //精确匹配username的用户对象
    User getByUsernameIs(String username);
} 

什么annotation都不用打,也不需要写实现。直接使用即可:

@Controller
public class TestController {

    @Autowired
    private UserRepository userRepository;

    @RequestMapping("/searchUser/{username}")
    public @ResponseBody
    List<User> searchUser(@PathVariable("username") String username) {
        List<User> result = this.userRepository.findByUsernameContaining(username);
        return result;
    }

}

方便到不可相信,如果在intellij还可以有提示,根本不需要记住命名规则,只要你懂点英文基本上intellij给你的提示就足够了。

Spring提供了非常大的自由度给开发者,我们可以在接口方法中通过定义@Query annotation自定义接口方法的JPQL语句,其实笔者对JPQL并不熟悉,但是笔者对HQL非常熟悉,其实笔者感觉两者基本上并没有什么差别,笔者在JPA开发过程当中直接都是在写JPQH的语句当中使用HQL也正常无误。以下是WeiboRepository的接口:

public interface WeiboRepository extends JpaRepository<Weibo,Long>,JpaSpecificationExecutor<Weibo> {

    @Query("select w from Weibo w where w.user.username = :username")
    List<Weibo> searchUserWeibo(@Param("username") String username);

    @Query("select w from Weibo w where w.user.username = :username")
    List<Weibo> searchUserWeibo(@Param("username") String username, Sort sort);

    @Modifying
    @Transactional(readOnly = false)
    @Query("update Weibo w set w.weiboText = :text where w.user = :user")
    int setUserWeiboContent(@Param("text")String weiboText,@Param("user")User user);

    Page<Weibo> findByUserIsAndWeiboTextContaining(User user, String weiboText, Pageable pageable);
@Transactional ( readOnly  =  false )  int  deleteByUser (User user) ; }

除了我们刚刚说过的@Query annotation之外,我这里还是用了Spring Data JPA提供的Sort和Pageable。暂时忽略JpaSpecificationExecutor<Weibo>接口的继承

Sort提供字段排序的功能,而Pageable则提供分页的功能。需要注意的是,如果使用了Pageable返回值是Page<T>的泛型对象,这个对象里面提供的分页信息相当丰富。足够使用。只要方法有Pageable参数就会自动实现分页,同时将总页面数和总对象数等信息都通过page对象一一返回。

以下是searchUserWeibo方法、findByUserIsAndWeiboTextLike方法的使用:

@RequestMapping("/username/{username}")
public List<Weibo> getUserWeibo(@PathVariable("username") String username) {
    return this.weiboRepository.searchUserWeibo(username,new Sort(new Sort.Order(Sort.Direction.DESC,"weiboId")));
}
Sort的使用非常简单,可以看到上述代码当中可以使用多个order进行排序。

@RequestMapping("/simpleSearch")
    public Page<Weibo> simpleSearch(String username,String weiboText,int pageNo,int pageSize){
        User user = this.userRepository.getByUsernameIs(username);
        return this.weiboRepository.findByUserIsAndWeiboTextContaining(user,weiboText,new PageRequest(pageNo,pageSize));
    }
Pageable是一个接口,而上述使用到的PageRequest是一个实现类。这里的pageNo是从0开始。Page<Weibo>的数据结构如下,以下是返回的Json字符串:

[plain]  view plain  copy
  1. {  
  2.     "content": [  
  3.         {  
  4.             "weiboId": 4,  
  5.             "user": {  
  6.                 "userId": 1,  
  7.                 "username": "TONY",  
  8.                 "userpwd": "TONYYAN"  
  9.             },  
  10.             "weiboText": "TONY's WEIBO_D!!!",  
  11.             "createDate": 1498751667000,  
  12.             "comments": []  
  13.         },  
  14.         {  
  15.             "weiboId": 5,  
  16.             "user": {  
  17.                 "userId": 1,  
  18.                 "username": "TONY",  
  19.                 "userpwd": "TONYYAN"  
  20.             },  
  21.             "weiboText": "TONY's WEIBO_E!!!",  
  22.             "createDate": 1498751667000,  
  23.             "comments": []  
  24.         }  
  25.     ],  
  26.     "last": true,  
  27.     "totalElements": 5,  
  28.     "totalPages": 2,  
  29.     "number": 1,  
  30.     "size": 3,  
  31.     "sort": null,  
  32.     "first": false,  
  33.     "numberOfElements": 2  
  34. }  
重点!!!当笔者尝试在命名式方法传入为空的对象时,Spring并没有自动排除为空的字段条件。

或者你可能会发现了如下的接口方法:

@Transactional(readOnly = false)
int deleteByUser(User user);
这个也是Spring提供的命名实现,我是根据intellij的提示打出来的,但是这个会发出很多条SQL:

Hibernate: select user0_.user_id as user_id1_2_, user0_.username as username2_2_, user0_.userpwd as userpwd3_2_ from users user0_ where user0_.username=?
Hibernate: select weibo0_.weibo_id as weibo_id1_3_, weibo0_.create_date as create_d2_3_, weibo0_.user_id as user_id4_3_, weibo0_.weibo_text as weibo_te3_3_ from weibo weibo0_ left outer join users user1_ on weibo0_.user_id=user1_.user_id where user1_.user_id=?
Hibernate: select comments0_.weibo_id as weibo_id5_0_0_, comments0_.comment_id as comment_1_0_0_, comments0_.comment_id as comment_1_0_1_, comments0_.comment_date as comment_2_0_1_, comments0_.comment_text as comment_3_0_1_, comments0_.user_id as user_id4_0_1_, comments0_.weibo_id as weibo_id5_0_1_, user1_.user_id as user_id1_2_2_, user1_.username as username2_2_2_, user1_.userpwd as userpwd3_2_2_ from comments comments0_ left outer join users user1_ on comments0_.user_id=user1_.user_id where comments0_.weibo_id=?
Hibernate: select comments0_.weibo_id as weibo_id5_0_0_, comments0_.comment_id as comment_1_0_0_, comments0_.comment_id as comment_1_0_1_, comments0_.comment_date as comment_2_0_1_, comments0_.comment_text as comment_3_0_1_, comments0_.user_id as user_id4_0_1_, comments0_.weibo_id as weibo_id5_0_1_, user1_.user_id as user_id1_2_2_, user1_.username as username2_2_2_, user1_.userpwd as userpwd3_2_2_ from comments comments0_ left outer join users user1_ on comments0_.user_id=user1_.user_id where comments0_.weibo_id=?
Hibernate: select comments0_.weibo_id as weibo_id5_0_0_, comments0_.comment_id as comment_1_0_0_, comments0_.comment_id as comment_1_0_1_, comments0_.comment_date as comment_2_0_1_, comments0_.comment_text as comment_3_0_1_, comments0_.user_id as user_id4_0_1_, comments0_.weibo_id as weibo_id5_0_1_, user1_.user_id as user_id1_2_2_, user1_.username as username2_2_2_, user1_.userpwd as userpwd3_2_2_ from comments comments0_ left outer join users user1_ on comments0_.user_id=user1_.user_id where comments0_.weibo_id=?
Hibernate: select comments0_.weibo_id as weibo_id5_0_0_, comments0_.comment_id as comment_1_0_0_, comments0_.comment_id as comment_1_0_1_, comments0_.comment_date as comment_2_0_1_, comments0_.comment_text as comment_3_0_1_, comments0_.user_id as user_id4_0_1_, comments0_.weibo_id as weibo_id5_0_1_, user1_.user_id as user_id1_2_2_, user1_.username as username2_2_2_, user1_.userpwd as userpwd3_2_2_ from comments comments0_ left outer join users user1_ on comments0_.user_id=user1_.user_id where comments0_.weibo_id=?
Hibernate: select comments0_.weibo_id as weibo_id5_0_0_, comments0_.comment_id as comment_1_0_0_, comments0_.comment_id as comment_1_0_1_, comments0_.comment_date as comment_2_0_1_, comments0_.comment_text as comment_3_0_1_, comments0_.user_id as user_id4_0_1_, comments0_.weibo_id as weibo_id5_0_1_, user1_.user_id as user_id1_2_2_, user1_.username as username2_2_2_, user1_.userpwd as userpwd3_2_2_ from comments comments0_ left outer join users user1_ on comments0_.user_id=user1_.user_id where comments0_.weibo_id=?
Hibernate: delete from comments where comment_id=?
Hibernate: delete from comments where comment_id=?
Hibernate: delete from comments where comment_id=?
Hibernate: delete from comments where comment_id=?
Hibernate: delete from comments where comment_id=?
Hibernate: delete from comments where comment_id=?
Hibernate: delete from comments where comment_id=?
Hibernate: delete from comments where comment_id=?
Hibernate: delete from comments where comment_id=?
Hibernate: delete from comments where comment_id=?
Hibernate: delete from weibo where weibo_id=?
Hibernate: delete from weibo where weibo_id=?
Hibernate: delete from weibo where weibo_id=?
Hibernate: delete from weibo where weibo_id=?
Hibernate: delete from weibo where weibo_id=?

它会一条条的发SQL去删。


在当然@Query不止是查询语言也可以用于更新和删除语句,不过需要额外添加@Modifying annotation,由于实验环境我就直接打上@Transactional声明式事务,在正式的开发环境请在你的业务层上写@Transactional,当然你也可以都写上,这样会按照你配置的或者是默认的事务传播机制进行事务传播:

@Modifying
@Transactional(readOnly = false)
@Query("update Weibo w set w.weiboText = :text where w.user = :user")
int setUserWeiboContent(@Param("text")String weiboText,@Param("user")User user);

除了@Query annotation 还可以在Entity类中使用@NamedQuery,定义查询语句。但是笔者觉得还是不及@Query来得直接方便。

@Entity
@Table(name = "users")
@NamedQuery(name = "User.searchUserName",query = "select u from User u where u.username like :username")
public class User {
UserRepository接口定义searchUserName方法即可:

public interface UserRepository extends JpaRepository<User,Long> {

    List<User> searchUserName(@Param("username") String username);


四、JpaSpecificationExecutor接口

还记得刚刚暂时忽略的JpaSpecificationExecutor<Weibo>接口继承吗?在WeiboRepository接口中继承JpaSpecificationExecutor<Weibo>接口就可以在外部使用Spring提供的Criteria查询,但是这个查询也存在一些问题~  以下就是一段范例:

@RequestMapping("/searchWeibo")
    public Page<Weibo> searchWeibo(final String username, final String weiboText, final Date startDate, final Date endDate,int pageNo,int pageSize) {
        Page<Weibo> page = this.weiboRepository.findAll(new Specification<Weibo>() {
            @Override
            public Predicate toPredicate(Root<Weibo> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                List<Predicate> predicates = new LinkedList<>();

                if (!StringUtils.isEmpty(username)) {
                    //Join有两种方式                   
//                    Join<Weibo,User> userJoin = root.join("user",JoinType.INNER);
//                    predicates.add(criteriaBuilder.equal(userJoin.get("username"), username));
                    predicates.add(criteriaBuilder.equal(root.get("user").get("username"),username));
                }
                if (!StringUtils.isEmpty(weiboText)) {
                    predicates.add(criteriaBuilder.like(root.get("weiboText"), "%" + weiboText + "%"));
                }
                if(startDate!=null){
                    predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get("createDate").as(Date.class),startDate));
                }
                if(endDate != null){
                    predicates.add(criteriaBuilder.lessThanOrEqualTo(root.get("createDate").as(Date.class),endDate));
                }
                return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
            }
        },new PageRequest(pageNo,pageSize));
        return page;
    }
可以直接使用findAll方法并创建匿名内部类继承Specification即可,实现toPredicate方法返回最终的条件,但是笔者想在这里使用JoinFetch,通过很多方法也未能在toPredicate实现,希望其他大牛走过路过,留下在这里使用joinFetch的方法。


五、普通的DAO

当然我们可以使用最为基础的方法去写DAO。直接@Repository 然后@PersistenceContext。最原始最基本~

@Repository
public class WeiboDao {

    @PersistenceContext
    private EntityManager entityManager;


    @Transactional(readOnly = true)
    public List<Weibo> searchWeiboByEm(String username, String weiboText, Date startDate, Date endDate, int pageNo, int pageSize) {
        StringBuffer jpql = new StringBuffer("select w from Weibo w join fetch w.user u left join fetch w.comments c where 1=1 ");
        Map<String,Object> paramMap = new HashMap<>();
        if(!StringUtils.isEmpty(username)){
            jpql.append(" and u.username = :username");
            paramMap.put("username",username);
        }
        if(!StringUtils.isEmpty(weiboText)){
            jpql.append(" and w.weiboText like :weiboText");
            paramMap.put("weiboText","%"+weiboText+"%");
        }
        if(startDate!=null){
            jpql.append(" and w.createDate >= :startDate");
            paramMap.put("startDate",startDate);
        }
        if(endDate != null){
            jpql.append(" and w.createDate <= :endDate");
            paramMap.put("endDate",endDate);
        }

        Query query = entityManager.createQuery(jpql.toString());
        Set<String> keys = paramMap.keySet();
        for (String keyItem : keys) {
            query.setParameter(keyItem,paramMap.get(keyItem));
        }
        return query.setFirstResult(pageNo*pageSize).setMaxResults(pageSize).getResultList();
    }

}

六、声明式事务

SpringBoot默认已经帮我们自动配置好了JPATransaction,我们直接使用即可。

直接在Service层测试@Transactional,当然正常的事务隔离级别还有事务传递方式还是和以前Spring一样配置。注意这个@Transactional是Spring提供的,不要错误的使用JPA的@Transactional

@Service
public class WeiboService {

    @Autowired
    private WeiboRepository weiboRepository;

    @Transactional(readOnly = false,isolation = Isolation.READ_COMMITTED)
    public List<Weibo> importWeiboList(List<Weibo> weibos, User user){
        int index = 0;
        Date nowDateTime = new Date(System.currentTimeMillis());
        for (Weibo weiboItem: weibos) {
            weiboItem.setUser(user);
            weiboItem.setCreateDate(nowDateTime);
            if(5<=index++){
                throw new RuntimeException("Weibo out of limit!!!");
            }
            this.weiboRepository.save(weiboItem);
        }
        return weibos;
    }

}
SpringBoot系列之Spring Data Jpa集成教程 SpringBoot系列之Spring Data Jpa集成教程 Spring Data Jpa是属于Spring Data的一个子项目,Spring data项目是一款集成了很多数据操作的项目,其下的子项目有如下图所示: 官方也有提供了英文版的文档,具体可以参考Spring data官方文档 JPA是一个规范,并非具体的实现框架,而Spring Data JPA就是依赖于Hibernate J... 阅读详情

相关推荐

关于石墨烯费米能级和偏置电压之间的关系问题

石墨烯费米能级与电压的关系问题

qq_37946542的博客 3514

在 Spring Boot 上配置 Tomcat 连接池

Spring-Boot 支持 HikariCP(默认)、tomcat-jdbc 和 Commons DBCP 作为数据库的连接池。但是,每个池都使用一组不同的属性。在本教程中,我们将学习如何配置 Tomcat 连接池。 配置 Tomcat JDBC 连接池 首先,要配置 Tomcat 连接池,您需要在application.properties文件的“spring.datasource.tomcat”命名空间中添加属性。这是一个例子: spring.datasource.tomca..

allway2的博客 4104

Python自然语言处理 NLTK 库用法入门教程【经典】

@本文来源于公众号:csdn2299,喜欢可以关注公众号 程序员学府 本文实例讲述了Python自然语言处理 NLTK 库用法。分享给大家供大家参考,具体如下: 在这篇文章中,我们将基于 Python 讨论自然语言处理(NLP)。本教程将会使用 Python NLTK 库。NLTK 是一个当下流行的,用于自然语言处理的 Python 库。 那么 NLP 到底是什么?学习 NLP 能带来什么好处? ...

python函数基础教程 1万+

【Spring Boot】Spring Boot Tomcat 连接池使用示例

文章目录示例工具版本Tomcat JDBC 的依赖Tomcat 连接池配置1. initialSize2. maxWait3. maxActive4. maxIdle5. minIdle6. defaultAutoCommit7. testOnBorrowSpring Boot Data + Tomcat JDBC Connection Pool + MySQL 使用示例测试应用程序1. 使用 Maven 命令2. 使用 Eclipse3. 使用可执行 JAR参考文献源码下载 本页将介绍Spring Boo

猫巳的博客 4368

IDEA集成selenium自动化测试(Java+Selenuim+testNG+Maven)

文章目录写在前面01 | 新建Maven项目02 | idea集成Selenium03 | 写在前面 因为软件测试的需求和博主比较经常使用Java,于是,想在Idea中集成selenuim,用Java编写测试代码,因此有了这篇文章来记录一下过程。 资源指路: java使用selenium实现模拟浏览器操作API大全 模拟登录 TestNG入门教程 感谢上面博客的大佬。 01 | 新建Maven项目 不使用骨架创建,直接点击左侧Maven,然后一路next即可。 02 | idea集成Sele

aLinxi的博客 8279

Android7以上抓取https数据包-无需root

手机内装一个虚拟机,然后在虚拟机内运行我需要抓包的应用,该虚拟机内是不需要证书验证的。完美解决android7.0以上https无法抓包的问题。

qq_45764684的博客 1454

配合spring 使用 tomcat连接池

准备修改pom.xml<!--mysql驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.40</version> </dependency><!--tomcat连接池--> <dependency> <group

hjx601496320 9449

谷粒商城学习笔记-15-数据库初始化

这一节的主要内容是把创建对应的数据库和数据库表。

epitomizelu的专栏 1438

SpringBoot---SpringBoot-JPA

JPA 应该都熟悉了,我就不多说了什么是JPA了。目前JPA主要实现由hibernate和openJPA等。Spring Data JPA 是Spring Data 的一个子项目,它通过提供基于JPA的Repository极大了减少了操作JPA的代码。笔者觉得这个由SpringBoot 提供的JPARepository真的是非常爽。基本上大部分的业务都可以满足了。在Spring环境中需要配置大量了...

haylee的专栏 1万+

Python爬虫实战:构建药监局药品批准信息本地查询系统

在医药信息化时代,药品批准信息的快速查询对于医药从业者、研究人员和普通消费者都具有重要意义。国家药品监督管理局(NMPA)提供了官方的药品查询系统,但有时我们需要建立本地数据库以便批量分析或离线查询。本文将详细介绍如何使用Python爬虫技术,结合最新的异步爬虫框架和技术栈,构建一个完整的药监局药品信息本地查询系统。本系统将实现以下功能:自动化爬取药监局官网的药品批准信息数据清洗和结构化存储建立本地查询接口数据定期更新机制pythonimport os"""药品类型枚举"""

2201_76125261的博客 726

YOLOv11的对比学习预训练改进-(通过SimCLR预训练增强特征提取)

本文介绍了将SimCLR对比学习预训练与YOLOv11目标检测模型相结合的创新方法。文章首先概述了目标检测领域的发展现状和YOLOv11的架构优化,包括主干网络、特征金字塔和检测头的改进设计。其次详细解析了SimCLR对比学习的核心原理,涵盖数据增强、投影头设计和NT-Xent损失函数。最后探讨了如何将对比学习与目标检测有效结合,通过特征空间对齐和渐进式微调策略提升模型性能。该方法显著增强了特征提取能力,提高了检测精度和训练效率,特别是在小目标和遮挡目标的检测上表现突出。

本博客聚焦 YOLOv11 全流程落地,涵盖架构优化、数据集处理、训练技巧与多场景部署,兼及国产数据库、Java 开发与 AI 模型应用,内容兼顾理论与工程实践,为开发者提供系统干货,助力高效提升技术能力。 685

spring boot(10)-tomcat jdbc连接池

默认连接池 tomcat jdbc是从tomcat7开始推出的一个连接池,相比老的dbcp连接池要优秀很多。spring boot将tomcat jdbc作为默认的连接池,只要在pom.xml中引入了spring boot 的jdbc组件,就会自动引入tomcat jdbc连接池。关于引入jdbc和数据库的连接配置,参考第六篇 默认参数 以下是org.apac...

weixin_30613433的博客 623

16、经皮功能性电刺激助行:Parastep系统全解析

本文全面解析了经皮功能性电刺激助行系统——Parastep系统,涵盖其发展历史、技术参数、系统设计、患者准入标准、训练方案及临床效果。作为首个获得FDA批准并商业化的非侵入性FES助行系统,Parastep为胸段截瘫患者提供了独立站立与行走的可能性,并在改善下肢血流、肌肉质量、心理状态等方面展现出显著医学与心理益处。文章还对比了其他FES系统,分析了Parastep的优势与局限,并探讨了未来技术改进方向与应用拓展前景。

l3m4n的博客 106

SpringBoot系列:6、整合JPA

1、Spring Data JPA介绍 1.1 Spring Data JPA介绍 Spring Data JPA(Java Persistence API),是Spring框架的主要构建块之一。如果您想使用持久数据,它也是一个强大的工具。包括以下三方面的内容: 一套API标准。在javax.persistence的包下面,用来操作实体对象,执行CRUD操作,框架在后台替代我们完成所有的事情,开...

程序员小H 1083

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

【Springboot系列】SpringBoot整合Jpa

Spring Boot是一种快速开发框架,它简化了Java应用程序的开发过程。而Jpa(Java Persistence API)是Java持久化规范的一种实现,它提供了一种方便的方式来访问和操作数据库。将Spring Boot与Jpa整合可以更加方便地进行数据库操作,提高开发效率。本文将介绍如何使用Spring Boot整合Jpa,帮助读者快速上手并应用于实际项目中。Jpa(Java Persistence API)是Java持久化规范的一种实现,它提供了一种方便的方式来访问和操作数据库。

Venus's Blog 1万+

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

SpringBoot集成jpa 一篇就够了 超详细

SpringBoot集成jpa 网上有很对jpa的介绍,但是都不是很全,这边根据公司的实际使用情况进行的总结。 JPA、Hibernate、Spring data jpa之间的关系 主要参考https://my.oschina.net/u/3080373/blog/1828589 大家可以读一下这篇文章 什么是JPA? 全称Java Persistence API,可以通过注解或者XML描述【对象-关系表】之间的映射关系,并将实体对象持久化到数据库中。 为我们提供了: 1)ORM映射元数据:JPA支持XML

cmx1060220219的博客 8万+

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

SpringBoot 一文搞懂Spring JPA

一文搞懂Spring JPA 什么是 JPA spirng data jpa是spring提供的一套简化JPA开发的框架,按照约定好的【方法命名规则】写dao层接口,就可以在不写接口实现的情况下,实现对数据库的访问和操作。同时提供了很多除了CRUD之外的功能,如分页、排序、复杂查询等等。 Spring Data JPA 可以理解为 JPA 规范的再次封装抽象,底层还是使用了 Hibernate 的 JPA 技术实现。 Springboot整合SpringData JPA SpringData简介 整合Sp

matafeiyanll的博客 1万+

什么是JPA?SpringBoot 中使用JPA

Java Persistence API)Java持久化API,是一套Sun公司Java官方制定的ORM(sun公司并没有实现)。(Object Relational Mapping)为对象关系映射,是一个实现使用对象操作数据库的设计思想。其作用是在操作数据库之前,先把数据表与实体类关联起来,然后通过实体类的对象操作(增删改查)数据库表。既然JPA是一套规范,也就意味着它只是一套实现ORM理论的接口,。那么我们必须要有具体的实现者。......

someday____的博客 7013

SpringBoot整合JPA

SpringBoot整合JPA

YIYIYI 1万+

SpringBoot系列教程JPA之update使用姿势

原文: 190623-SpringBoot系列教程JPA之update使用姿势 上面两篇博文拉开了jpa使用姿势的面纱一角,接下来我们继续往下扯,数据插入db之后,并不是说就一层不变了,就好比我在银行开了户,当然是准备往里面存钱了,有存就有取(特别是当下银行利率这么低还不如买比特币屯着,截止19年6月22日,btc已经突破1.1w$,可惜没钱买

小灰灰blog的专栏 3万+

SpringBoot系列教程JPA之新增记录使用姿势

SpringBoot系列教程JPA之新增记录使用姿势 上一篇文章介绍了如何快速的搭建一个JPA的项目环境,并给出了一个简单的演示demo,接下来我们开始业务教程,也就是我们常说的CURD,接下来进入第一篇,如何添加数据 通过本篇文章,你可以get到以下技能点 POJO对象如何与表关联 如何向DB中添加单条记录 如何批量向DB中添加记录 save 与 saveAndFlush的区别 I....

小灰灰blog的专栏 3689

【SpringBoot新手篇】SpringBoot集成JPA

SpringBoot整合JPASpring Boot Jpa 介绍首先了解 Jpa 是什么?Spring Data JPA特征整合SpringData JPAPomymlentityRepositoryUserServiceUserServiceImplcontroller首页展示高级查询功能预先生成方法自定义简单查询复杂查询分页查询限制查询 Spring Boot Jpa 介绍 首先了解 Jpa 是什么? Jpa (Java Persistence API)是 Sun官方提出的Java 持久化规范。它为

输入技术、输出想法 3900

SpringBoot集成JPA用法笔记

今天给大家整理SpringBoot集成JPA用法。希望对大家能有所帮助!搭建SpringBoot项目 ...

IT技术分享社区 844

SpringBoot集成JPA及基本使用

本篇从JDBC、ORM、JPA、Spring Data JPA说起,分享了SpringBoot集成JPA的实现、Repository的命名规则以及InvalidDefinitionException异常的处理

JingAi_jia917的博客 5557
上一篇: Spring Data JPA 之 一对一,一对多,多对多 关系映射
下一篇: vue项目中实现扫码功能
CSC_Cockroach
博客等级 码龄10年 7粉丝 19原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值