用了一段时间的Spring,到现在也只是处于会用的状态,对于深入一点的东西都不太了解。所以决定开始深入学习Spring。

本文主要记录JPA学习。在学习JPA之前,需要了解一些ORM的概念。

ORM概念:

对象关系映射ORM(Object/Relation Mapping)是一种为了解决面向对象和关系数据之间存在互不匹配现象的技术(范式不匹配)。简而言之,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到数据库中。本质上是将数据从一种形式转换到另外一种形式。(个人考虑:但是这么做,感觉好像和一般方法比起来会有额外的开销吧?待解决)

范式不匹配:

  1. 问题1:对象之间相互管理,且这些管理又有方向信息。如果关联是双向的,那么两个对象之间可以相互导航。在关系表中联系可以通过外键设置,但是外键并没有保存任何方向信息。
  2. 问题2:粒度。在一个类和一个表之间可能没有确切的一对一的映射。例如一个用户表可以用来保存用户数据及其联系方式,家庭住址等信息。但是在对对象进行建模的时候,可以使用多个独立的类,如用户类,地址类,联系方式类等。所以在面向对象中,对象模型的粒度更趋向于细化,而数据库里则更希望表之间的两句数量更少。
  3. 问题3:继承和多态。我们可以创建多个不同类型的对象层次结构,可以建立抽象类型之间的关联,从而在运行时使用多个具体类生产的对象关联。然而在关系数据里没有层次结构的概念,所以只能在表之间建立外键关系。为了处理多态,必须创建一个表到多个不同表的外键关系。
  4. 问题4:对象按需遍历和关系数据的获取方法之间互不适用。在对象世界,关系是通过节点到节点的方式遍历的,所以访问目标对象时,才需要适用改对象的状态。在关系世界里,最好的做法是通过连接表减少SQL查询的数量,从而使用最少的查询量就可以获取所需数据。很明显,这样做与对象按需遍历是冲突的,可能导致程序端内存问题。而另一方面,如果以表到表方式获取数据,那数据库端会产生性能问题。

ORM具备功能:

一般ORM框架包括以下四个功能:

  1. 对象模型和关系模型之间的元数据映射
  2. 一个队持久类对象进行CRUD操作的API
  3. 一种对象查询语言
  4. 不同的数据获取策略和对象网络遍历功能,以便提高内存使用率已经数据获取时间性能

映射发生情况:

对象-关系映射一般发生在以下情况:

  1. 类和表
  2. 类的属性和表中的列
  3. 对象关联和外键
  4. Java类型和SQL类型

以上就是JPA的一些概念,接下来上JPA。

JPA功能:

JPA(Java persistence API)主要用用于Java中处理持久化操作。对ORM特性和功能进行标准化。分别定义了用来将对象模型映射到关系模型的API、可以在对象上执行CRUD操作、一种对象查询语言以及通过对象图获取数据的标准API。

JPA好处:???都还没怎么用,没法做出比较。还是先使用一段时间后,再来评价

使用JPA:

定义实体:

实体定义:一个对象在数据库端与一个带有主键的记录相对应,那么该对象就称为实体。

下面代码定义了一个最简单的实体类:

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table; /**
*
* <p>ClassName: User.java<p>
* <p>用户实体类<p>
* @author linzj
* @createTime 2016年3月17日 下午1:08:35
*/
@Entity
@Table(name="user")
public class User { @Id
@GeneratedValue
private Long id; }

JPA为映射提供了java注解,可以发现这些注解都在javax.persistence包下。上面的@Entity注解定义了User类为持久化类型,并有一个对应的表。@Table注解指定了表名,如果没有指定表明则默认使用类名为其表名。

@Id注解标记了主键属性。如果主键列名与属性名不匹配,则可以使用@Column来指定对应的主键列名,如添加上@Column(name=”uid”),就与uid列表对应。应用程序并不负责生成主键值,而是在新记录插入期间由JPA供应商分配(如Hibernate)。通过@GenerateValue注解告诉JPA,应用程序将不会负责分配主键值,而是有JPA供应商处理。

属性映射到列:

为了更加直观的理解。我先建了一张User表:

mysql> create table user(
-> id int(5) not null primary key auto_increment,
-> username varchar(60) not null,
-> passwd varchar(60) not null);
Query OK, 0 rows affected (0.14 sec) mysql> describe user;
+----------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+----------------+
| id | int(5) | NO | PRI | NULL | auto_increment |
| username | varchar(60) | NO | | NULL | |
| passwd | varchar(60) | NO | | NULL | |
+----------+-------------+------+-----+---------+----------------+

接下来我们就可以将刚刚的User实体类添加对应的属性,并将属性映射到上面这张user表中对应的列了:

/**
*
* <p>ClassName: User.java<p>
* <p>用户实体类<p>
* @author linzj
* @createTime 2016年3月17日 下午1:08:35
*/
@Entity
@Table(name="user")
public class User { @Id
@GeneratedValue
private Long id; private String username; @Column(name = "passwd")
private String password; @Transient
private String others;
}

因为表中没有password这个字段,所以我们必须指定映射的列名,即指定password映射到passwd。我们还发现实体类中多了一个表里没有的属性others,注意这个others属性已经注解为@Transient,因此others属性将被JPA忽略,不会进行映射。

对象之间创建关联:

数据库关联类型有:

  • 一对一(1:1)
  • 多对一(M:1)
  • 一对多(1:M)
  • 多对多(M:M)

一对一关联:

/**
*
* <p>ClassName: User.java<p>
* <p>
* 用户实体类,通过@OneToOne注解标识一对一关联,
* @JoinColumn注解更加User和Adress实体指定了
* 表之间的外键关系。用户表中的列引用了地址表
* <p>
* @author linzj
* @createTime 2016年3月17日 下午1:08:35
*/
@Entity
@Table(name="user")
public class User { @Id
@GeneratedValue
private Long id; private String username; @Column(name = "passwd")
private String password; @OneToOne
@JoinColumn(name = "adress_id")//添加外键
private Adress adress;
} /**
*
* <p>ClassName: Adress.java<p>
* <p>地址实体类<p>
* @author linzj
* @createTime 2016年3月17日 下午1:52:30
*/
@Entity
public class Adress {
//...pass
}

多对一:

/**
*
* <p>ClassName: Employe.java<p>
* <p>
* 雇员实体类,使用了@ManyToOne注解,并使用了@JoinColumn指定两表之间的外键关系,
* optional=false 标识关联是Employe必须的,即:Employe不能单独存在。
* <p>
* @author linzj
* @createTime 2016年3月17日 下午1:55:11
*/
@Entity
public class Employe { //...pass @ManyToOne(optional = false)
@JoinColumn(name = "company_id")
private Company company; } @Entity
public class Company {
//...pass
}

多对一:

/**
*
* <p>ClassName: Student.java<p>
* <p>
* 学生实体类,@OneToMany注解在学生和书本之间创建了一对多的关系,
* 用@JoinColumn指定外键,不同的是这次外键列存在于Book实体中。
* <p>
* @author linzj
* @createTime 2016年3月17日 下午2:01:23
*/
@Entity
public class Student { //...pass
@OneToMany
@JoinColumn(name = "student_id")
private Set<Book> books = new HashSet<Book>(); } @Entity
public class Book { //...pass }

多对多:

/**
*
* <p>ClassName: Product.java<p>
* <p>
* 产品实体类,@ManyToMany创建多对多关联,
* 注意这里用的是@JoinTable而不是@JoinColumn,
* 因为一边的多个实例可以与另一边的多个实例相关联。因此无法再一个列中保存指向另一个表的关联数据。
* 所以这里就需要提供一个关联表,该表包含每个表的主键列引用。
* 本示例中,关联表是product_catalog,joinColumns和inverseJoinColumns指定
* product_catalog表中的列名,joinColumns指定Product表主键的引用,inverseJoinColumns
* 指定Catalog表主键的引用。
* <p>
* @author linzj
* @createTime 2016年3月17日 下午2:07:47
*/
@Entity
public class Product {
@Id
@GeneratedValue
private Long id; @ManyToMany(cascade=CascadeType.ALL)
@JoinTable(name = "product_catalog", joinColumns = @JoinColumn(name = "product_id"),
inverseJoinColumns = @JoinColumn(name = "catalog_id"))
private Set<Catalog> catalog = new HashSet<Catalog>(); public Long getId() {
return id;
} public void setId(Long id) {
this.id = id;
} public Set<Catalog> getCatalog() {
return catalog;
} public void setCatalog(Set<Catalog> catalog) {
this.catalog = catalog;
}
} @Entity
public class Catalog {
@Id
@GeneratedValue
private Long id;
}
/**
对应的表如下:
mysql> describe product_catalog;
+------------+------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+------------+------+-----+---------+-------+
| product_id | bigint(20) | NO | PRI | NULL | |
| catalog_id | bigint(20) | NO | PRI | NULL | |
+------------+------------+------+-----+---------+-------+
2 rows in set (0.01 sec) mysql> select * from product_catalog;
+------------+------------+
| product_id | catalog_id |
+------------+------------+
| 1 | 1 |
| 1 | 2 |
+------------+------------+
2 rows in set (0.00 sec) mysql> select * from product;
+----+
| id |
+----+
| 1 |
+----+
1 row in set (0.00 sec) mysql> select * from catalog;
+----+
| id |
+----+
| 1 |
| 2 |
+----+
*/

关联的方向性:

关联的方向性只有两种:单向和双向。

在单向关联中,只能从关联的源对象到目标对象。双向关联则处理能从关联的源对象到目标对象外,还可以从目标对象到源对象。

在如下代码中,我们只是将上面的Adress实体类修改了下,就构成了双向关联。我们在Adress实体类代码中,添加了@OneToOne(mappedBy = "adress"),并且添加了mappedBy属性,通过该属性,JPA可以识别该属性,从而管理两个实体之间的关联。使用mappedBy属性的一边可以看作成一个镜子,或者是只读的。它并不会对数据库的数据产生影响。拿下面的代码说明就是,用下面的实体类做持久化操作后,数据库的Adress表里并不会多一列关于user的字段。

@Entity
@Table(name="user")
public class User { @Id
@GeneratedValue
private Long id; private String username; @Column(name = "passwd")
private String password; @OneToOne
@JoinColumn(name = "adress_id")//添加外键
private Adress adress; public Long getId() {
return id;
} public void setId(Long id) {
this.id = id;
} public String getUsername() {
return username;
} public void setUsername(String username) {
this.username = username;
} public String getPassword() {
return password;
} public void setPassword(String password) {
this.password = password;
} public Adress getAdress() {
return adress;
} public void setAdress(Adress adress) {
this.adress = adress;
} } @Entity
public class Adress {
@Id
@GeneratedValue
private Long id; @OneToOne(mappedBy = "adress")
private User user; public Long getId() {
return id;
} public void setId(Long id) {
this.id = id;
} public User getUser() {
return user;
} public void setUser(User user) {
this.user = user;
} }

上面代码放在我的GITHUB

另外提供一些关于JPA的博客:

http://www.cnblogs.com/holbrook/archive/2012/12/30/2839842.html

http://www.cnblogs.com/chenying99/p/3143516.html

以上是本人学习JPA的记录,下一篇介绍配置和使用JPA。

Spring学习---JPA学习笔记的更多相关文章

  1. Spring Data JPA 学习记录1 -- 单向1:N关联的一些问题

    开新坑 开新坑了(笑)....公司项目使用的是Spring Data JPA做持久化框架....学习了一段时间以后发现了一点值得注意的小问题.....与大家分享 主要是针对1:N单向关联产生的一系列问 ...

  2. spring data jpa 学习笔记

    springboot 集成 springData Jpa 1.在pom.xml添加依赖 <!-- SpringData-Jpa依赖--> <dependency <groupI ...

  3. Spring学习笔记(八)Spring Data JPA学习

    ​ jpa简单的命名规则如下,这个不多做介绍,放在这里也是给自己以后查找起来方便,这篇文章主要介绍之前一直忽略了的几个点,像@NoRepositoryBean这个注解,以及怎么自定义Repositor ...

  4. Spring Data JPA学习笔记

    下面先来介绍一下JPA中一些常用的查询操作: //And --- 等价于 SQL 中的 and 关键字,比如 findByHeightAndSex(int height,char sex): publ ...

  5. SpringBoot学习笔记:Spring Data Jpa的使用

    更多请关注公众号 Spring Data Jpa 简介 JPA JPA(Java Persistence API)意即Java持久化API,是Sun官方在JDK5.0后提出的Java持久化规范(JSR ...

  6. spring data jpa入门学习

    本文主要介绍下spring data jpa,主要聊聊为何要使用它进行开发以及它的基本使用.本文主要是入门介绍,并在最后会留下完整的demo供读者进行下载,从而了解并且开始使用spring data ...

  7. spring cloud(学习笔记)高可用注册中心(Eureka)的实现(二)

    绪论 前几天我用一种方式实现了spring cloud的高可用,达到两个注册中心,详情见spring cloud(学习笔记)高可用注册中心(Eureka)的实现(一),今天我意外发现,注册中心可以无限 ...

  8. JPA学习笔记(8)——映射一对多关联关系

    一对多关联关系 本文有很多和多对一是一样的,因此不会写得非常具体. 有看不懂的.能够參考JPA学习笔记(7)--映射多对一关联关系 Order实体类 package com.jpa.helloworl ...

  9. spring 中bean学习笔记

    spring 中bean 一.bean的定义和应用 1. bean 形象上类似于getXX()和setXX()的一种. 2. 由于java是面向对象的,类的方法和属性在使用中需要实例化. 3. 规律: ...

随机推荐

  1. php 编程效率(1)

    用单引号代替双引号来包含字符串,这样做会更快一些.因为PHP会在双引号包围的字符串中搜寻变量,单引号则 不会,注意:只有echo能这么做,它是一种可以把多个字符串当作参数的“函数”(译注:PHP手册中 ...

  2. .gitigore 相关

    为什么要配置.gitigore 在我们使用git的过程当中,不是任何文件都需要commit到本地或者远程仓库的,比如一些三方库文件.那么作为一个git新手,很多人不知道如何配置.gitignore文件 ...

  3. ReactiveCocoa & FRP & MVVM

    Functional Reactive Programming(以下简称FRP)是一种响应变化的编程范式.先来看一小段代码 a = 2 b = 2 c = a + b // c is 4 b = 3 ...

  4. android Button获取焦点

    有时直接使用requestFocus()不能给button设置焦点,经网上查找得到如下结论: 先setFocus,再requestFocus.                 btn.setFocus ...

  5. java 持有对象

    1.泛型和类型安全的容器 ArrayList,可以自动扩充大小的数组,add插入对象,get访问对象,size查看对象数目. 1 /** 2 * 泛型和类型安全的容器 3 * 2016/5/6 4 * ...

  6. USACO Section 1.3 Ski Course Design 解题报告

    题目 题目描述 有N座山,每座山都有一个高度,现在由于农夫想避税,所以想把这些山的高度进行一些改变,使得最高的山与最低的山之间的高度差不超过17.每座山最多只能改变一次高度,每次改变高度都会产生一定的 ...

  7. RedHat Enterprise Linux AS4&5 安装gcc过程

    三.Gcc安装方法(redhat 4): 一.安装步骤 1.使用which gcc命令查看gcc是否安装安装 2.如若没有安装则下载如下安装包,所需安装包如下 一共需要拷贝以下五个安装包: binut ...

  8. python中uuid来生成机器唯一标识

    摘要: 我们可以使用uuid1的后16位来标识一个机器.  # use machine specific uuid, last 16 char will be the same if machine ...

  9. codeforces 369 div2 C dp

    http://codeforces.com/contest/711 C. Coloring Trees time limit per test 2 seconds memory limit per t ...

  10. android脚步---将layout和其输出一起保存成图片

    public void convertViewToBitmap(View view) { //View view = getLayoutInflater().inflate(R.layout.test ...