SpringBoot JPA学习概要总结

JPA是什么?

其实JPA也不是第一天听说,Coding中也用了一段时间了,突然想起来JPA到底是什么东西,想干什么? 似乎很高深的样子^_^, 自己反而说不敢肯定地说这东西是什么,感觉查了一把,原来就是三个字:ORM, 还好,没有理解错(惭愧,这段时间经常有点信心缺失的感觉)。 下面是一些官样文字:

JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。
应该是SUN提出来的。
JPA由EJB 3.0软件专家组开发,作为JSR-220实现的一部分。
这是一个概念框架,而不是实现。
Hibernate3.2+、TopLink 10.1.3以及OpenJPA都提供了JPA的实现
JPA包括以下3方面的技术:
- ORM映射元数据 JPA支持XML和JDK5.0注解两种元数据的形式,元数据描述对象和表之间的映射关系
- API 用来操作实体对象,执行CRUD操作,框架在后台替代我们完成所有的事情,开发者从繁琐的JDBC和SQL代码中解脱出来
- 查询语言 通过面向对象而非面向数据库的查询语言查询数据,避免程序的SQL语句紧密耦合


Hibernate和JPA的关系

一般用JPA时,使用的是 javax.persistence.xxx, 但是有些类,在Hibernate中也有定义,该用谁呢? 网上的说法是:当需要扩展的时候,Hibernate会提供自己的对象,但一般都是继承了JPA的。


EntityManager是干什么的?

先看这篇文章JavaEE – JPA(4):EntityManager相关核心概念

public interface EntityManager {
    public void persist(Object entity);
    public <T> T merge(T entity);
    public void remove(Object entity);
    public <T> T find(Class<T> entityClass, Object primaryKey);
    // ......
}

我的理解:EntityManager只是定义了持久化Entity的接口,通过这些接口可以保存和读取对象,但是对象的管理是有另外一个叫PersistenceContext的对象来管理的,类似于内存池的管理,多个EM对象可以使用同一个PC对象。

遗留问题: 在SpringBoot中如何操作EntityManager?


SpringBoot中使用JPA+Mysql

  1. 项目文件配置
    我的项目是用Gradle配置的
compile("org.springframework.boot:spring-boot-starter-data-jpa")
compile 'mysql:mysql-connector-java'
  1. 资源文件配置

    spring:
    datasource:
    url: jdbc:mysql://localhost:3306/test_db
    username: root
    password: xxxxx
    jpa:
    show-sql: true

    使用这个配置,没有指定ddl属性,对于mysql来说,不会做任何事情,不会自动创建表也不会删除表,这其实是项目中常用的模式,先建表,再写代码^_^。

    这里还有个小插曲,在src/main目录下建了一个resources目录后,把application.yml放进去,运行起来报错,似乎是没有认到这个资源文件,一开始百思不得其解,后来无意中发现,要在build.gradle上点右键–refresh gradle project,然后eclipse就会resource目录和src/main/java/显示在一起,再运行就正常了。


Entity的定义和使用

一般的Entity类定义如下:

@Entity(name = "t_student")
public class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    public Long id;

    public String name;

我不太喜欢写get,set,直接就把变脸定义为public。

注解 作用
@Entity 指定表的名字, mysql上的结果来看,表名用大写,小写,混写都没有关系
@Id 指定ID字段,JPA要求每一个实体Entity,必须有且只有一个主键,这个注解没属性需要设置
@GeneratedValue 和@Id配合使用,指定主键的生成策略,详见2, 一般来说就是自增长/序列号这种,JPA默认的是Auto,会自动根据使用的数据库来确定策略,那么mysql就是自增长,oracle就是序列了。
@Column 指定字段名,有name, unique, nullable,insertable,table等属性. 实践中,大多可以不用这个注解来显式指定字段名,JPA会根据默认规则去进行映射:
1. 表字段名为单个单词,比如name, 那么entity属性直接用字段名就好了;
2. 表字段名带下划线,比如target_name, 那么entity属性就是targetName
3. 在Repo中定义的方法中引用属性名的时候,也是按此规则做反向解析。

Repository的概述

Repository真的是什么都不干,仅仅定了一个接口

public interface Repository<T, ID extends Serializable> {
}

接下来有几个子类扩展了Repository,实现不同的操作。

类名 内容
CrudRepository 继承知Repository,定义了基本的CRUD方法,我们也可以扩展,比如findByxxxx
PagingAndSortingRepository 继承自CrudRepository,支持排序和分页
JpaRepository 继承自PagingAndSortingRepository,QueryByExampleExecutor 扩展的功能待研究

##自定义Repository
有几种方式来扩展Repository

  1. 直接用字段名来指定查询条件,比如findBy, deleteBy, 可以使用的语法可以参考官方文档4
  2. @NamedQuery, @Query注解,自己写sql语句

    我喜欢用@Query, 这种方式不好的地方就是数据字段名改了的话得跟着改sql语句。

  3. select的例子

    @Query("select new com.test.Student (h.no, h.name) from t_student h")
    List<Student> getAllStudent();

    有时候我们只是需要返回表中的部分字段,此时需要在entity中定义对应参数的构造方法。

  4. update的例子

    @Transactional
    @Modifying(clearAutomatically = true)
    @Query(value = "update t_student p set p.name=?2 where p.id=?1", nativeQuery = true)
    int updateName(long id, String name);

不想写原生SQL怎么办?

对于简单的场景,直接写sql,简单明了,但是从工程角度来说,维护性又差了一点, 比如表的字段名改了就要跟着改sql语句。
JPA提供了几种方式来处理这个问题,这篇文章做了介绍,就个人来说,比较喜欢Querydsl。


QueryDsl

这篇文章,我觉得讲得比较详细spring boot-jpa整合QueryDSL来简化复杂操作

环境的建立 如果用的是Gradle,可以参考我的另外一篇文章来配置环境。

今天在简书上看到一位仁兄恒宇少年写的QueryDsl系列文章,非常地详细,本来我也想整理类似文章,看来用不着了,大家请移步:http://www.jianshu.com/p/99a5ec5c3bd5

Spring Boot JPA - 使用 Querydsl 处理复杂的操作 这篇文章也不错


返回自定义字段

很多时候,我们的查询是从几个关联表里面各自取几个字段作为结果返回,但是这些字段怎么映射为DTO对象返回给用户呢?

  • 如果是JPA,那得自己写SQL语句, 在其中new DTO,把结果字段作为new的参数;
  • 如果用QueryDsl,有Project方法来帮助完成这个转换。

事务处理

使用起来比较简单,App上加@EnableTransactionManagement,然后在需要的方法上加@Transactional就行了。
详细内容可以参考catoop写的这篇文章-Spring Boot 事务的使用


L1 Cache

普遍的说法是L1 Cache和EntityManage挂在一起的,那就有个问题了,我们的工程中,EM对象只有一个,如果每次查询都把数据cache起来,那且不是数据量很大,而且现在大家都搞分布式开发,同一个工程部署在多个机器上,在其中一台机器上对一个对象做update,另外一台机器上如果前面有该对象的cache,且不是不会知道这个变化,从而一直都是返回旧的对象? 这个疑问,在头脑里面想了很久,一直不太明白, 今天写了点代码来测试了一下,结果发现不会有这种情况,缓存是跟单次请求的执行过程或者说线程挂在一起的,一个外部请求过来,系统必然分配一个线程去执行,这一次执行过程中的结果会cache,结束以后感觉就释放掉了。下一次即使是同样的请求再来,又起一个线程来执行,是不会用到上次的cache结果的,会首先从数据库中读取数据。而且如果你执行过程中,启动另外一个新线程,那这个线程的cache是独立的, 这样有可能会导致问题,反而需要注意:
1. 主线程读取entity,结果会cache;
2. 启动子线程,对该entity做update;
3. 再主线程中再次读取,返回的是cache结果。

当然实际代码中,这种写法应该非常稀罕。

另外这个L1 Cache只有findOne这个方法执行的时候才会自动把结果cache起来,我们自己写的sql语句的结果是不会保存的。(findById,findAll感觉都没有cache)。


本Markdown编辑器使用StackEdit修改而来,用它写博客,将会带来全新的体验哦:

  • Markdown和扩展Markdown简洁的语法
  • 代码块高亮
  • 图片链接和图片上传
  • LaTex数学公式
  • UML序列图和流程图
  • 离线写博客
  • 导入导出Markdown文件
  • 丰富的快捷键

快捷键

  • 加粗 Ctrl + B
  • 斜体 Ctrl + I
  • 引用 Ctrl + Q
  • 插入链接 Ctrl + L
  • 插入代码 Ctrl + K
  • 插入图片 Ctrl + G
  • 提升标题 Ctrl + H
  • 有序列表 Ctrl + O
  • 无序列表 Ctrl + U
  • 横线 Ctrl + R
  • 撤销 Ctrl + Z
  • 重做 Ctrl + Y

Markdown及扩展

Markdown 是一种轻量级标记语言,它允许人们使用易读易写的纯文本格式编写文档,然后转换成格式丰富的HTML页面。 —— [ 维基百科 ]

使用简单的符号标识不同的标题,将某些文字标记为粗体或者斜体,创建一个链接等,详细语法参考帮助?。

本编辑器支持 Markdown Extra ,  扩展了很多好用的功能。具体请参考Github.

表格

Markdown Extra 表格语法:

项目 价格
Computer $1600
Phone $12
Pipe $1

可以使用冒号来定义对齐方式:

项目 价格 数量
Computer 1600 元 5
Phone 12 元 12
Pipe 1 元 234

定义列表

Markdown Extra 定义列表语法:
项目1
项目2
定义 A
定义 B
项目3
定义 C

定义 D

定义D内容

代码块

代码块语法遵循标准markdown代码,例如:

@requires_authorization
def somefunc(param1='', param2=0):
    '''A docstring'''
    if param1 > param2: # interesting
        print 'Greater'
    return (param2 - param1 + 1) or None
class SomeClass:
    pass
>>> message = '''interpreter
... prompt'''

脚注

生成一个脚注1.

目录

[TOC]来生成目录:

数学公式

使用MathJax渲染LaTex 数学公式,详见math.stackexchange.com.

  • 行内公式,数学公式为:Γ(n)=(n1)!nN
  • 块级公式:

x=b±b24ac2a

更多LaTex语法请参考 https://docs.spring.io/spring-data/jpa/docs/current/reference/html/“>这儿.

UML 图:

可以渲染序列图:

Created with Raphaël 2.1.0张三张三李四李四嘿,小四儿, 写博客了没?李四愣了一下,说:忙得吐血,哪有时间写。

或者流程图:

Created with Raphaël 2.1.0开始我的操作确认?结束yesno
  • 关于 序列图 语法,参考 这儿,
  • 关于 流程图 语法,参考 这儿.

离线写博客

即使用户在没有网络的情况下,也可以通过本编辑器离线写博客(直接在曾经使用过的浏览器中输入write.blog.csdn.net/mdeditor即可。Markdown编辑器使用浏览器离线存储将内容保存在本地。

用户写博客的过程中,内容实时保存在浏览器缓存中,在用户关闭浏览器或者其它异常情况下,内容不会丢失。用户再次打开浏览器时,会显示上次用户正在编辑的没有发表的内容。

博客发表后,本地缓存将被删除。 

用户可以选择 把正在写的博客保存到服务器草稿箱,即使换浏览器或者清除缓存,内容也不会丢失。

注意:虽然浏览器存储大部分时候都比较可靠,但为了您的数据安全,在联网后,请务必及时发表或者保存到服务器草稿箱

浏览器兼容

  1. 目前,本编辑器对Chrome浏览器支持最为完整。建议大家使用较新版本的Chrome。
  2. IE9以下不支持
  3. IE9,10,11存在以下问题
    1. 不支持离线功能
    2. IE9不支持文件导入导出
    3. IE10不支持拖拽文件导入


  1. 这里是 脚注内容.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章