JPA基本使用

JPA的基本使用

这段时间看了下JPA,简单的做个笔记吧。

1、搭建环境

1.1、application.yaml

我用的是Mysql,需要数据库连接驱动,数据源看你心情吧,系统也有默认的数据源。

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/examinedb?useSSL=false&allowPublicKeyRetrieval=true
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
  jpa:
    database: MYSQL
    show-sql: true
    open-in-view: true
    properties:
      hibernate:
        enable_lazy_load_no_trans: true #使用延时加载时控制Session的生命周期
        dialect: org.hibernate.dialect.MySQL5Dialect
        ddl-auto: update

  thymeleaf:
    cache: false

secure:
  ignored:
    urls:
      - /swagger-resources/**
      - /v2/api-docs/**
      - /swagger-ui.html
      - /*.html
      - /**/*.html
      - /**/*.css
      - /**/*.js
      - /**/*.png
      - /**/*.jpg
server:
  port: 81

1.2、构建ORM关系映射模型

@Entity标注是JPA的实体,@Table和数据库哪一个表做的映射。@ManyToMany(targetEntity = Role.class)标识多对多,同理一对多,一对一也是基本这个套路。不同点在于,多对多要把中间表描述出来,把支撑三表连接的连接列表明出来。

package com.cbf.pojo;

import lombok.Data;

import javax.persistence.*;
import java.io.Serializable;
import java.util.List;

/**
 * @Description  
 * @Author  wangnaixing
 * @Date 2021-12-25 12:27:03 
 */
@Data
@Entity
@Table ( name ="t_user" , schema = "")
public class User  implements Serializable {

	private static final long serialVersionUID =  5450815487641580632L;

	/**
	 * 自增主键
	 */
	@Id
   	@Column(name = "id" )
	@GeneratedValue(strategy=GenerationType.AUTO)
	private Long id;

	/**
	 * 用户名
	 */
   	@Column(name = "username" )
	private String username;

	/**
	 * 密码
	 */
   	@Column(name = "pwd" )
	private String pwd;

	/**
	 * 角色集合
 	 */
	@ManyToMany(targetEntity = Role.class)
	@JoinTable(name = "t_user_role_relation",joinColumns = {@JoinColumn(name = "user_id")},inverseJoinColumns = {@JoinColumn(name="role_id")})
	private List<Role> roleList;
	

}

1.3、实现接口

啥都不用写了,太爽了。人家会在你启动SpringBoot项目的时候,自动的生成实现类。

package com.cbf.repo;

import com.cbf.pojo.Role;
import org.springframework.data.repository.CrudRepository;

/**
 * @author by wangnaixing
 * @Description
 * @Date 2021/12/25 13:26
 */
public interface RoleDao extends CrudRepository<Role,Long> {
}

2、增删改查实现

在Controller层,本人直接注入的JPA的Dao.来操作CRUD,可看出增删改查都有现成的方法,用就可以了。唯独分页,需要Pageable对象,了解到你的分页信息。在查询方法中,只要带上这个参数执行查询的集合出来就会分页,你可以使用org.springframework.data.domain.Page(SpringBoot)自带的分页结果容器接受到这些分页信息,比如当前页,总页码,分页记录,之类的这些。

1、添加

  /**
     * 新增学生
     * @param entity
     * @return
     */
    @PostMapping("/save")
    public void save(Student entity){
        log.info("==系统正在新增学生==");
        studentDao.save(entity);
        log.info("==系统成功新增学生!==");

    }

2、修改

   /**
     * 修改学生
     */
    @GetMapping("/update/{id}")
    public void update(Student entity){
        log.info("==系统正在修改学生==");
        studentDao.save(entity);
        log.info("==系统成功修改学生!==");
    }

3、删除

 /**
     * 删除学生
     * @param id
     */
    @GetMapping("/delete/{id}")
    public void delete(@PathVariable(name = "id") Long id){
        log.info("==系统正在删除学生==");
        studentDao.deleteById(id);
        log.info("==系统成功删除学生!==");
    }

4、根据ID查询

 /**
     * 根据Id查询学生信息
     * @param id
     */
    @GetMapping("/findById/{id}")
    public String findById(@PathVariable(name = "id") Long id, Model model){
        log.info("==系统正在查询学生==");
        Student student = studentDao.findById(id).orElse(null);
        model.addAttribute("student",student);
        log.info("==系统成功查询学生!==");
        return "forward:/toEditStudentPage";
    }

4、分页

因为本人角色,使用SpringBoot的分页信息对象直接返回前端不合理,其理由在于有太多不必要的参数了,我通常会定义一个分页交互模型,把需要的数据抽离,从Page抽离出来,再给到前端。

    /**
     * 分页查询
     * @param pageNo
     * @param pageSize
     * @return
     */
    @RequestMapping("/findByPage")
    public String findByPage(
            @RequestParam(name = "pageNo",defaultValue = "0") Integer pageNo,
            @RequestParam(name = "pageSize",defaultValue = "5") Integer pageSize, Model model){
        log.info("==系统查询学生==");
        //分页查询
        List<Student> studentList = new ArrayList<>();
        Pageable pageable = PageRequest.of(pageNo, pageSize);
        CommonPage<Student> page = CommonPage.restPage(studentDao.findAll(pageable));
        //结果存入请求域
        model.addAttribute("page",page);
        //结果存入请求域
        model.addAttribute("studentList",studentList);

        //转发的学生list页面
        log.info("==系统成功查询学生!==");
        return "/student/student_list";
    }
package com.cbf.repo;

import com.cbf.pojo.Student;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.CrudRepository;

/**
 * @author by wangnaixing
 * @Description
 * @Date 2021/12/25 10:07
 */
public interface StudentDao extends CrudRepository<Student,Long> {

    /**
     * 分页查询
     * @param pageable
     * @return
     */
    Page<Student> findAll(Pageable pageable);
}

package com.cbf.common;

import org.springframework.data.domain.Page;

import java.util.List;


/**
 * 分页数据封装类
 * @author wangnaixing
 * @param <T>
 */
 @Data
public class CommonPage<T> {
    private Integer pageNum;
    private Integer pageSize;
    private Integer totalPage;
    private Long total;
    private List<T> list;


    /**
     * 将SpringData分页后的list转为分页信息
     */
    public static <T> CommonPage<T> restPage(Page<T> pageInfo) {
        CommonPage<T> result = new CommonPage<T>();
        result.setTotalPage(pageInfo.getTotalPages());
        result.setPageNum(pageInfo.getNumber());
        result.setPageSize(pageInfo.getSize());
        result.setTotal(pageInfo.getTotalElements());
        result.setList(pageInfo.getContent());
        return result;
    }


}

3、生成实体类的插件

比较头疼的事情是什么,就是ORM模型构建,这个表创建好了,一个一个创建对象,再加注解,简直是搞自己心态吧。其实呢,IDEA已经给我们提供了插件。

两步就能自动生成Model模型了。

3.1、第一步:配置

找到表,右键到序号3的编辑器中,覆盖文件内容。文件内容如下。仔细看配置,你就能明白这个脚本,做一些自己的调整,和MyBatis Genernate MyBatis Plus 那种生成针对的方面也差不多,啥包位置了,作者啦,去表前缀了。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LHop7Ohz-1640484782622)(D:\my_notebook\typora笔记图片统一管理处\image-20211226100411122.png)]

import com.intellij.database.model.DasTable
import com.intellij.database.model.ObjectKind
import com.intellij.database.util.Case
import com.intellij.database.util.DasUtil

import java.text.SimpleDateFormat

/*
 * Available context bindings:
 *   SELECTION   Iterable<DasObject>
 *   PROJECT     project
 *   FILES       files helper
 */
packageName = ""
typeMapping = [
        (~/(?i)tinyint|smallint|mediumint/)      : "Integer",
        (~/(?i)int/)                             : "Long",
        (~/(?i)bool|bit/)                        : "Boolean",
        (~/(?i)float|double|decimal|real/)       : "BigDecimal",
        (~/(?i)datetime|timestamp|date|time/)    : "Date",
        (~/(?i)blob|binary|bfile|clob|raw|image/): "InputStream",
        (~/(?i)/)                                : "String"
]


FILES.chooseDirectoryAndSave("Choose directory", "Choose where to store generated files") { dir ->
    SELECTION.filter { it instanceof DasTable && it.getKind() == ObjectKind.TABLE }.each { generate(it, dir) }
}

def generate(table, dir) {
    def className = javaClassName(table.getName(), true)
    def fields = calcFields(table)
    packageName = getPackageName(dir)
    PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(new FileOutputStream(new File(dir, className + ".java")), "UTF-8"))
    printWriter.withPrintWriter { out -> generate(out, className, fields, table) }

//    new File(dir, className + ".java").withPrintWriter { out -> generate(out, className, fields,table) }
}

// 获取包所在文件夹路径
def getPackageName(dir) {
    return dir.toString().replaceAll("\\\\", ".").replaceAll("/", ".").replaceAll("^.*src(\\.main\\.java\\.)?", "") + ";"
}

def generate(out, className, fields, table) {
    out.println "package $packageName"
    out.println ""
    out.println "import javax.persistence.Column;"
    out.println "import javax.persistence.Entity;"
    out.println "import javax.persistence.Table;"
    out.println "import javax.persistence.Id;"
    out.println "import javax.persistence.GeneratedValue;"
    out.println "import java.io.Serializable;"
    Set types = new HashSet()

    fields.each() {
        types.add(it.type)
    }

    if (types.contains("Date")) {
        out.println "import java.util.Date;"
    }

    if (types.contains("BigDecimal")) {
        out.println "import java.math.BigDecimal;"
    }

    if (types.contains("InputStream")) {
        out.println "import java.io.InputStream;"
    }
    out.println ""
    out.println "/**\n" +
            " * @Description  \n" +
            " * @Author  wangnaixing\n" + //1. 修改idea为自己名字
            " * @Date " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + " \n" +
            " */"
    out.println ""
    out.println "@Entity"
    out.println "@Table ( name =\"" + table.getName() + "\" , schema = \"\")" //2. schema = \"后面添加自己的表空间名称(mysql可以不添加, 不用这个schema属性也行)
    out.println "public class $className  implements Serializable {"
    out.println ""
    out.println genSerialID()
    fields.each() {
        out.println ""
        // 输出注释
        if (isNotEmpty(it.commoent)) {
            out.println "\t/**"
            out.println "\t * ${it.commoent.toString()}"
            out.println "\t */"
        }

        if ((it.annos+"").indexOf("[@Id]") >= 0) out.println "\t@Id"

        if (it.annos != "") out.println "   ${it.annos.replace("[@Id]", "")}"


        // 输出成员变量
        out.println "\tprivate ${it.type} ${it.name};"
    }

    // 输出get/set方法
    fields.each() {
        out.println ""
        out.println "\tpublic ${it.type} get${it.name.capitalize()}() {"
        out.println "\t\treturn this.${it.name};"
        out.println "\t}"
        out.println ""

        out.println "\tpublic void set${it.name.capitalize()}(${it.type} ${it.name}) {"
        out.println "\t\tthis.${it.name} = ${it.name};"
        out.println "\t}"
    }

    // 输出toString方法
    out.println ""
    out.println "\t@Override"
    out.println "\tpublic String toString() {"
    out.println "\t\treturn \"{\" +"
    fields.each() {
        out.println "\t\t\t\t\t\"${it.name}='\" + ${it.name} + '\\'' +"
    }
    out.println "\t\t\t\t'}';"
    out.println "\t}"

    out.println ""
    out.println "}"
}

def calcFields(table) {
    DasUtil.getColumns(table).reduce([]) { fields, col ->
        def spec = Case.LOWER.apply(col.getDataType().getSpecification())

        def typeStr = typeMapping.find { p, t -> p.matcher(spec).find() }.value
        def comm = [
                colName : col.getName(),
                name    : javaName(col.getName(), false),
                type    : typeStr,
                commoent: col.getComment(),
                annos   : "\t@Column(name = \"" + col.getName() + "\" )"]
        if ("id".equals(Case.LOWER.apply(col.getName())))
            comm.annos += ["@Id"]
        fields += [comm]
    }
}

// 这里是处理数据库表前缀的方法,这里处理的是t_xxx命名的表
// 已经修改为使用javaName, 如果有需要可以在def className = javaName(table.getName(), true)中修改为javaClassName
// 处理类名(这里是因为我的表都是以t_命名的,所以需要处理去掉生成类名时的开头的T,
// 如果你不需要去掉表的前缀,那么请查找用到了 javaClassName这个方法的地方修改为 javaName 即可)
def javaClassName(str, capitalize) {
    def s = com.intellij.psi.codeStyle.NameUtil.splitNameIntoWords(str)
            .collect { Case.LOWER.apply(it).capitalize() }
            .join("")
            .replaceAll(/[^\p{javaJavaIdentifierPart}[_]]/, "_")
    // 去除开头的T  http://developer.51cto.com/art/200906/129168.htm
    s = s[1..s.size() - 1]
    capitalize || s.length() == 1 ? s : Case.LOWER.apply(s[0]) + s[1..-1]
}

def javaName(str, capitalize) {
//    def s = str.split(/(?<=[^\p{IsLetter}])/).collect { Case.LOWER.apply(it).capitalize() }
//            .join("").replaceAll(/[^\p{javaJavaIdentifierPart}]/, "_")
//    capitalize || s.length() == 1? s : Case.LOWER.apply(s[0]) + s[1..-1]
    def s = com.intellij.psi.codeStyle.NameUtil.splitNameIntoWords(str)
            .collect { Case.LOWER.apply(it).capitalize() }
            .join("")
            .replaceAll(/[^\p{javaJavaIdentifierPart}[_]]/, "_")
    capitalize || s.length() == 1 ? s : Case.LOWER.apply(s[0]) + s[1..-1]
}

def isNotEmpty(content) {
    return content != null && content.toString().trim().length() > 0
}

static String changeStyle(String str, boolean toCamel) {
    if (!str || str.size() <= 1)
        return str

    if (toCamel) {
        String r = str.toLowerCase().split('_').collect { cc -> Case.LOWER.apply(cc).capitalize() }.join('')
        return r[0].toLowerCase() + r[1..-1]
    } else {
        str = str[0].toLowerCase() + str[1..-1]
        return str.collect { cc -> ((char) cc).isUpperCase() ? '_' + cc.toLowerCase() : cc }.join('')
    }
}

//生成序列化的serialVersionUID
static String genSerialID() {
    return "\tprivate static final long serialVersionUID =  " + Math.abs(new Random().nextLong()) + "L;"
}


3.2、第二步:生成POJO到指定位置

一张图,自己摸索吧。

在这里插入图片描述

项目文件在我提供的资源可以下载看到源码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值