利用切面关闭spring-data-jpa的自动更新入库

当用了事务后,用jpa从数据库中查出来的实体对象,直接变更实体对象属性值,即使不调用repository.save()方法也会自动更新入库,下面讲讲怎么利用切面编来关闭JPA的自动更新入库

pom文件



    4.0.0
    
        com.flynn
        study
        0.0.1-SNAPSHOT
    
    com.flynn
    jpa-demo
    0.0.1-SNAPSHOT
    jpa-demo
    http://maven.apache.org
    
        UTF-8
    
    
        
            org.springframework.boot
            spring-boot-starter-data-jpa
        
                
            org.springframework.boot
            spring-boot-starter-web
        
        
            org.springframework.boot
            spring-boot-starter-test
            test
        
        
            mysql
            mysql-connector-java
            runtime
        

        
            com.alibaba
            druid
        
        
            org.projectlombok
            lombok
        
        
            junit
            junit
        
    
    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    

用到的语句

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` bigint NOT NULL,
  `address` varchar(255) DEFAULT NULL,
  `age` int DEFAULT NULL,
  `email` varchar(255) DEFAULT NULL,
  `gender` smallint NOT NULL,
  `name` varchar(255) DEFAULT NULL,
  `nick_name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

-- ----------------------------
-- Records of user
-- ----------------------------
BEGIN;
INSERT INTO `user` VALUES (1, '广州市', 16, '[email protected]', 1, 'test01', '天上人间00');
INSERT INTO `user` VALUES (2, '广州市', 18, '[email protected]', 2, 'test02', '天上人间00');
COMMIT;

SET FOREIGN_KEY_CHECKS = 1;

定义实体

package org.jpa.demo.entity;

import java.io.Serializable;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@Entity
public class User implements Serializable {

    private static final long serialVersionUID = 7940549851075553325L;
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Long id;
    private String name;
    private String nickName;
    private Integer age;
    private String email;
    private short gender;
    private String address; 
}

repository定义

package org.jpa.demo.repository;

import java.util.List;

import org.jpa.demo.entity.User;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface UserRepository extends CrudRepository{
    @Query(value = "select * from user", nativeQuery = true)
    List getUsers();
}

MyRunner实现接口CommandLineRunner

启动服务会执行run()函数里的逻辑

package org.jpa.demo.runner;

import java.util.List;
import java.util.Optional;

import javax.transaction.Transactional;

import org.jpa.demo.ResponseData;
import org.jpa.demo.entity.User;
import org.jpa.demo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import lombok.extern.slf4j.Slf4j;


@Slf4j
@Component
public class MyRunner implements CommandLineRunner{

    @Autowired
    private UserRepository userRepository;

    @Transactional
    @Override
    public void run(String... args) throws Exception {
        log.info("MyRunner ... ... start");
        
        log.info("userRepository.getUsers:");
        List users = userRepository.getUsers();
        
        log.info("update:");
        users.forEach(user->{
            String beforeUser = user.toString();
            user.setName(user.getName()+"-u");
            String afterUser = user.toString();
            log.info("update: {} => {}",beforeUser,afterUser);
        });
        log.info("MyRunner ... ... end");
    }
}

启动类

package org.jpa.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class JpaDemoApplication 
{
    public static void main( String[] args )
    {
        SpringApplication.run(JpaDemoApplication.class, args);
    }
}

启动服务,看看效果

  • 控制台日志
2022-09-17 17:17:33.248  INFO 94326 --- [           main] org.jpa.demo.JpaDemoApplication          : Started JpaDemoApplication in 4.279 seconds (JVM running for 5.332)
2022-09-17 17:17:33.269  INFO 94326 --- [           main] org.jpa.demo.runner.MyRunner             : MyRunner ... ... start
2022-09-17 17:17:33.269  INFO 94326 --- [           main] org.jpa.demo.runner.MyRunner             : userRepository.getUsers:
Hibernate: select * from user
2022-09-17 17:17:33.340  INFO 94326 --- [           main] org.jpa.demo.runner.MyRunner             : update:
2022-09-17 17:17:33.340  INFO 94326 --- [           main] org.jpa.demo.runner.MyRunner             : update: User(id=1, name=test01, nickName=天上人间00, age=16, [email protected], gender=1, address=广州市) => User(id=1, name=test01-u, nickName=天上人间00, age=16, [email protected], gender=1, address=广州市)
2022-09-17 17:17:33.340  INFO 94326 --- [           main] org.jpa.demo.runner.MyRunner             : update: User(id=2, name=test02, nickName=天上人间00, age=18, [email protected], gender=2, address=广州市) => User(id=2, name=test02-u, nickName=天上人间00, age=18, [email protected], gender=2, address=广州市)
2022-09-17 17:17:33.340  INFO 94326 --- [           main] org.jpa.demo.runner.MyRunner             : MyRunner ... ... end
Hibernate: update user set address=?, age=?, email=?, gender=?, name=?, nick_name=? where id=?
Hibernate: update user set address=?, age=?, email=?, gender=?, name=?, nick_name=? where id=?

从日志上看有执行update,更新了表里的数据

  • 表里的数据
1   广州市 16  [email protected] 1   test01-u    天上人间00
2   广州市 18  [email protected] 2   test02-u    天上人间00

看表里的数据,确实把表里的数据更新了,而代码里并没有调用类似repository.save()这样的方法来更新入库,JPA自动入库了

为了让JPA不自动入库,这里定义一个切面来处理

  • 定义个切面
package org.jpa.demo.aspect;

import java.util.Iterator;
import java.util.Optional;

import javax.persistence.Entity;
import javax.persistence.EntityManager;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.hibernate.Session;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import lombok.extern.slf4j.Slf4j;


@Slf4j
@Aspect
@Component
public class RepositoryAspect {

    @Autowired
    private EntityManager entityManager;
    
    @Pointcut("execution(public * org.jpa.demo.repository..*.*(..))*")
    public void repositoryEvict() {}
    
//  @After("repositoryEvict()")
//  public void doAfter() {
////        entityManager.flush();
//      entityManager.clear();
//      log.info("Aspect repositoryEvict()  doAfter ... ...");
//  }
    
    @Around("repositoryEvict()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        Object result = joinPoint.proceed();
        clear(result);
        log.info("Aspect repositoryEvict(),{}",joinPoint.getSignature());
        return result;
    }
    
    @SuppressWarnings({ "unchecked", "rawtypes" })
    private void clear(Object result) {
        if (result.getClass().getAnnotation(Entity.class)!=null) {
            evict(result);
            return;
        }
        
        if (result instanceof Optional) {
            Object entity = ((Optional)result).orElse(null);
            if(entity!=null && entity.getClass().getAnnotation(Entity.class)!=null) {
                evict(entity);
            }
            return;
        }
        
        if ((result instanceof Iterable)) {
            Iterator iterator = ((Iterable)result).iterator();
            boolean init = false;
            boolean toClear = false;
            while (iterator.hasNext()) {
                Object object = (Object) iterator.next();
                if (!init) {
                    toClear = object.getClass().getAnnotation(Entity.class)!= null;
                }
                if (!toClear) {
                    return;
                }
                evict(object);
            }
            return;
        }
    }
    
    private void evict(Object entity) {
        Session session = (Session)entityManager.getDelegate();
        if (session.isOpen()) {
            session.evict(entity);
        }
    }
}
  • 看看效果

日志

2022-09-17 17:24:11.576  INFO 97485 --- [           main] org.jpa.demo.runner.MyRunner             : MyRunner ... ... start
2022-09-17 17:24:11.576  INFO 97485 --- [           main] org.jpa.demo.runner.MyRunner             : userRepository.getUsers:
Hibernate: select * from user
2022-09-17 17:24:11.653  INFO 97485 --- [           main] org.jpa.demo.aspect.RepositoryAspect     : Aspect repositoryEvict(),List org.jpa.demo.repository.UserRepository.getUsers()
2022-09-17 17:24:11.653  INFO 97485 --- [           main] org.jpa.demo.runner.MyRunner             : update:
2022-09-17 17:24:11.653  INFO 97485 --- [           main] org.jpa.demo.runner.MyRunner             : update: User(id=1, name=test01-u, nickName=天上人间00, age=16, [email protected], gender=1, address=广州市) => User(id=1, name=test01-u-u, nickName=天上人间00, age=16, [email protected], gender=1, address=广州市)
2022-09-17 17:24:11.653  INFO 97485 --- [           main] org.jpa.demo.runner.MyRunner             : update: User(id=2, name=test02-u, nickName=天上人间00, age=18, [email protected], gender=2, address=广州市) => User(id=2, name=test02-u-u, nickName=天上人间00, age=18, [email protected], gender=2, address=广州市)
2022-09-17 17:24:11.654  INFO 97485 --- [           main] org.jpa.demo.runner.MyRunner             : MyRunner ... ... end

日志里有更新user.name属性,逻辑上有改变属性值(没有调用repository.save()进行入库)
test01-u => test01-u-u
test02-u => test02-u-u

表里的数据

1   广州市 16  [email protected] 1   test01-u    天上人间00
2   广州市 18  [email protected] 2   test02-u    天上人间00

从表的数据可以看出,确实没有入库,这样就可以关闭spring-data-jpa的自动更新入库

你可能感兴趣的:(利用切面关闭spring-data-jpa的自动更新入库)