zoukankan      html  css  js  c++  java
  • Spring学习---JPA学习笔记

      用了一段时间的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。

  • 相关阅读:
    Ubuntu使用之Svn命令小技巧
    Android
    Python&amp;MySQL&amp;PyQt
    YII进行数据增删改查分析
    UVA270-Lining Up
    block高级功能
    HDU-2665-Kth number(划分树)
    Android模糊演示样例-RenderScript-附效果图与代码
    HTTP状态码具体解释
    近期写的一个控件——Well Swipe beta 1.0
  • 原文地址:https://www.cnblogs.com/BigDreamer/p/5287722.html
Copyright © 2011-2022 走看看