Spring全家桶实践-数据操作JPA

知识贮备

Spring Data JPA官方网站
JPA规范与ORM框架之间的关系
Spring Data JPA与JPA规范的关系
Spring Data JPA中常用的注解

实践过程

目标:实现AOP日志记录到数据库中

数据用户权限设置,需要有创建表的权利,这里把库的权限都先给springsaas用户

    grant all privileges on springsaas.* to springsaas@'10.1.60.%' identified by 'springsaas';
    flush privileges;

引入依赖包

        
        
            org.springframework.boot
            spring-boot-starter-data-jpa
        
        
        
            mysql
            mysql-connector-java
        
        
        
            org.apache.commons
            commons-dbcp2
        

配置application.properties 新增数据库,连接池,JPA配置

说明:

  • com.mysql.jdbc.Driver已经废弃了,使用新的com.mysql.cj.jdbc.Driver
  • spring.datasource.url username password 根据实际情况自行设置
  • 连接池有很多,例如:c3p0,hikari,druid,dbcp2等等,本次使用的是dbcp2
#项目相关配置
server.port=8080
#服务名称
spring.application.name=springsaas
#数据库配置
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/springsaas?characterEncoding=UTF-8
spring.datasource.username=springsaas
spring.datasource.password=springsaas
##设置使用那个连接池
spring.datasource.type=org.apache.commons.dbcp2.BasicDataSource
#DBCP2配置(连接池)
##初始化连接池idle数量
spring.datasouece.dbcp2.initial-size=10
##连接最小idle数量
spring.datasource.dbcp2.min-idle=10
##连接最大idle数量
spring.datasource.dbcp2.max-idle=30
##连接超时的等待时间(30秒)
spring.datasource.dbcp2.max-wait-millis=30000
##轮询间隔时间,检测需要关闭的数据库连接 3分钟
spring.datasource.dbcp2.time-between-eviction-runs-millis=180000
#配置jpa
##配置数据库类型
spring.jpa.database=mysql
##配置是否输出sql语句
spring.jpa.show-sql=true
##ddl方式
#1、create:启动时删除上一次生成的表,并根据实体类生成表,表中数据会被清空。
#2、create-drop:启动时根据实体类生成表,sessionFactory关闭时表会被删除。
#3、update:启动时会根据实体类生成表,当实体类属性变动的时候,表结构也会更新,在初期开发阶段使用此选项。
#4、validate:启动时验证实体类和数据表是否一致,在我们数据结构稳定时采用此选项。
#5、none:不采取任何措施。
spring.jpa.hibernate.ddl-auto=update

#让控制器输出的json字符串格式更美观。
spring.jackson.serialization.indent-output=true

目录结构

Spring全家桶实践-数据操作JPA_第1张图片
logging目录结构

实现源码

改造domain 实体类(bean)和数据表进行映射,并且配置好映射关系,粗浅的类比为pojo

package com.springboot.action.saas.common.logging.domain;

import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.CreationTimestamp;

import javax.persistence.*;
import java.sql.Timestamp;
/*
* 日志数据和数据表实体类
* */
@Data
//jpa 实体
@Entity
//表名
@Table(name="mkt_log")
//lombok 注解,NoArgsConstructor 无参数构造函数
@NoArgsConstructor
public class Log {
    //设置ID主键,定义生成策略为自增
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    //描述
    private String description;
    //方法名
    private String method;
    //参数,定义字段类型为text
    @Column(columnDefinition = "text")
    private String params;
    //日志类型, 定义字段名称
    @Column(name = "log_type")
    private String logType;
    //请求ip, 定义字段名称
    @Column(name = "request_ip")
    private String requestIp;
    //请求耗时
    private Long time;
    //异常详细,定义字段名称
    @Column(name = "exception_detail", columnDefinition = "text")
    private String exceptionDetail;
    //请求时间,实体插入数据库的时候设置这个数值
    @CreationTimestamp
    @Column(name = "create_time")
    private Timestamp createTime;
    //构造函数(代参)
    public Log(String logType, Long time) {
        this.logType = logType;
        this.time = time;
    }
}

新建Repository 接口来操作实体类对应的数据表,粗浅的理解为Dao

package com.springboot.action.saas.common.logging.repository;

import com.springboot.action.saas.common.logging.domain.Log;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;

/*
* JpaRepository继承了接口PagingAndSortingRepository和QueryByExampleExecutor。
* PagingAndSortingRepository又继承CrudRepository。
* JpaRepository接口同时拥有基本CRUD功能以及分页功能。
* 官方的给的定义
* public interface JpaRepository extends PagingAndSortingRepository, QueryByExampleExecutor
* T 实例类
* ID id的数据类型
* */
@Repository
public interface LogRepository extends JpaRepository {
}

改造service impl,在上一个tag版本里面增加了记录数据库的功能

增加私有成员变量 logRepository
逻辑增加日志记录到数据库 logRepository.save(log);

package com.springboot.action.saas.common.logging.service.impl;

import com.springboot.action.saas.common.logging.domain.Log;
import com.springboot.action.saas.common.logging.repository.LogRepository;
import com.springboot.action.saas.common.logging.service.LogService;
import com.springboot.action.saas.common.utils.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

@Service
public class LogServiceImpl implements LogService {
    //数据操作类,相当于DAO
    @Autowired
    private LogRepository logRepository;

    /*
    * 记录日志接口实现
    **/
    @Override
    public void save(ProceedingJoinPoint joinPoint, Log log) {
        //获取request 请求对象
        HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes())
                .getRequest();
        //getSignature获取切面相关信息,比如方法名、目标方法参数等信息
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //获取抽象类(代理对象)方法
        Method method = signature.getMethod();
        //返回该元素的指定类型的注释,这里是Log注解
        com.springboot.action.saas.common.logging.annotation.Log aopLog = method.getAnnotation(com.springboot.action.saas.common.logging.annotation.Log.class);
        //获取注解传递的参数
        if (log != null) {
            log.setDescription(aopLog.value());
        }
        //通过最笨的反射方法,获取方法路径
        String methodName = joinPoint.getTarget().getClass().getName()+"."+signature.getName()+"()";
        log.setMethod(methodName);
        //参数处理
        //获取参数值
        Object[] argValues = joinPoint.getArgs();
        //获取参数名
        String[] argNames = ((MethodSignature)joinPoint.getSignature()).getParameterNames();
        //组织参数列表
        String params = "{";
        if(argValues != null){
            for (int i = 0; i < argValues.length; i++) {
                params += " " + argNames[i] + ": " + argValues[i];
            }
        }
        log.setParams(params + " }");
        //获取IP地址
        log.setRequestIp(StringUtils.getIP(request));
        //输出日志到控制台
        System.out.println(log.toString());
        //日志记录到数据库
        logRepository.save(log);
    }
}

运行测试效果

控制台输出sql,启动会创建表

Using dialect: org.hibernate.dialect.MySQL5Dialect
Hibernate: create table mkt_log (id bigint not null auto_increment, create_time datetime, description varchar(255), exception_detail text, log_type varchar(255), method varchar(255), params text, request_ip varchar(255), time bigint, primary key (id)) engine=MyISAM

数据库springsaas会创建mkt_log,当有自定义@Log注解的接口被访问请求后,表就会插入一条数据。

打标签

打tag 1.0.5版本,提交代码。

git tag -a v1.0.5 -m "实现日志信息通过JPA记录到数据库"

git push origin v1.0.5

github地址:https://github.com/horacepei/springsaas

你可能感兴趣的