# Notes

# Administrative Stufff 阅读材料

Goncalves Examples from Java EE 7

Extra Reading (you decide if you want/need)

  • Antonio Goncalves, Beginning Java EE 7, Chapter 4
  • Antonio Goncalves, Beginning Java EE 7, Chapter 5
  • Galvin Library Books 24x7

# Thinking Ahead to your FP 期末项目思考

  • Think of a domain or model you would like to work with going forward
    考虑您想继续使用的领域或模型

    • Must support multiple Entities
      必须支持多个实体
    • Must support the idea of relationships between Entities
      必须支持实体之间的关系的想法
    • Your overall idea should support 2 different types of users
      您的总体思路应支持 2 种不同类型的用户
    • Specs will follow so you have time to develop your thoughts and ideas along with material
      规格将随之而来,因此您有时间开发自己的思想和观念以及材料
  • One of the questions on your midterm will be to document your design
    期中问题之一就是记录您的设计

  • You will introduce your model over the next 2 labs, and continue working with it all semester
    您将在接下来的两个实验中介绍您的模型,并在整个学期中继续使用它

# Relational Databases vs OOP 关系数据库与 OOP

Relational Databases 关系数据库
  • Store data in tables consisting of rows and columns
    将数据存储在由行和列组成的表中
  • Primary keys 主键
  • Relationships with other tables via foreign keys or join tables
    通过外键或联接表与其他表的关系
OOP
  • Objects inherit state and behavior from other objects
    对象从其他对象继承状态和行为
  • Can contain other objects as state
    可以包含其他对象作为状态
  • Have references to collections of other objects
    引用了其他对象的集合

# JPA

Java Persistence API 的简称,中文名 Java 持久层 API,是 JDK 5.0 注解或 XML 描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。

# JPA Specification 规范

  • A specification not an implementation 规范而不是实现
    • EclipseLink is the current reference implementation EclipseLink 是当前的参考实现
    • History
  • Abstraction Layer over JDBC and SQL
    JDBC 和 SQL 上的抽象层
  • javax.persistence package
  • JPA Provides
    • Object Relational Mapping (to map relational databases to an object model - entities)
      对象关系映射(将关系数据库映射到对象模型 - 实体)
    • Entity Manager (manage entities, i.e. CRUD)
      实体管理器(管理实体,即 CRUD)
    • JPQL
    • Transactions and Locking
      交易和锁定
    • Lifecycle for persistent objects
      持久对象的生命周期
    • Database and/or script generation
      数据库和 / 或脚本生成

# JDBC vs JPA

  • JPA is built on top of JDBC JPA 建立在 JDBC 之上
  • Compare and Constrast 比较和对比

# JPA Entities 实例

  • Live short-term in memory 短期记忆
  • Persistently in database 永久存在数据库中
  • POJOs with annotations: 带注释的 POJOs
@Entity
public class Book {
    @Id @GeneratedValue
    private Long id;
    private String title;
    private Float price;
    private String description;
    private String isbn;
    private Integer nbOfPage;
    private Boolean illustrations;
    public Book() {
    }
    // Getters, setters
}

# Entity Manager 实体管理

  • Central piece of JPA
    JPA 的核心部分
  • API to find, query, create, remove and synchronize entities with databases
    用于查找、查询、创建、删除实体,并将其与数据库同步的 API
  • Manages state and life cycle of entities
    管理实体的状态和生命周期
  • EntityManager is an interface implemented by a persistence provider (like EclipseLink)
    EntityManager 是由持久性提供程序(例如 EclipseLink)实现的接口
  • Each EntityManager instance is associated with a persistence context
    每个 EntityManager 实例都与一个持久性环境相关联

# Persistence Context 持久环境

  • A persistence context is a set of managed entity instances at a given time for a given user’s transaction
    持久性环境,是一组在指定时间给指定用户的管理实体实例
  • Persistence Identities are unique within context
    持久性身份在上下文中是独一无二的
  • A first-level cache. Objects live in the persistence context for the duration of the transaction, and are then flushed to the database.
    一级缓存。对象在交易期间处于持久性环境中,然后冲洗到数据库。

# Obtaining an Entity Manager 创建一个实体管理

  • Application Managed (Lab 5) 应用管理
    • Persistence.createEntityManagerFactory("foo")
    • Requires EntityTransaction

Listing 6-2 in Goncalves

  • Container Managed (Lab 7) 容器管理
    • @PersistenceContext(unitName="foo")
    • Uses JTA for transaction

Listing 6-3 in Goncalves

# Manipulating Entities - CRUD 操纵实体 - CRUD

C(reate)
EntityManager.persist(e) adds an entity to the current persistence context and inserts to the database if it doesn’t already exist
R(ead)
  • EntityManager.find(pk) finds an entity and adds it to the current persistence context
  • EntityManager.getReference(pk) find an entity and adds it to the current persistence context, but lazily fetches its data
U(update)
  • EntityManager.merge(e) reattaches a detached entity to the current persistence context and updates the database
  • Updating a managed Entity through setters
D(delete)
EntityManager.remove(e) deletes an entity from the database and detaches it from the current persistence context. Will be GC’d.
Synchronizing 同步
  • EntityManager.flush(e) entity to database, no commit
  • EntityManager.refresh(e) entity from database (overwrite in memory state)
Persistence Context
  • EntityManager.contains(e) an entity in the current persistence context
  • EntityManager.clear() empties the current persistence context
  • EntityManager.detach(e) removes an entity from the current persistence context

Chapter 6, Goncalves Ex 03

# Entity Life Cycle 生命周期

Pet.java
@Entity
@Table(name = "pet")
@NamedQuery(name = "Pet.findPetByName", query = "select p from Pet p where p.name = :NAME")
@NamedQuery(name = "Pet.findAll", query = "select p from Pet p")
public class Pet {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @NotBlank
    @Column(name = "pet_name", nullable = false, unique = true)
    private String name;
    @PastOrPresent
    private LocalDate birthDate;
    @Transient
    private Integer age;
    
    @Enumerated(EnumType.STRING)
    private PetType type;
    public Pet() {
    }
    public Pet(String name, LocalDate birthDate, PetType type) {
        this.name = name;
        this.birthDate = birthDate;
        this.type = type;
    }
    /**
     * Get the value of age
     *
     * @return the value of age
     */
    public Integer getAge() {
        return age;
    }
    /**
     * Set the value of age
     *
     * @param age new value of age
     */
    public void setAge(Integer age) {
        this.age = age;
    }
    /**
     * Get the value of birthDate
     *
     * @return the value of birthDate
     */
    public LocalDate getBirthDate() {
        return birthDate;
    }
    /**
     * Set the value of birthDate
     *
     * @param birthDate new value of birthDate
     */
    public void setBirthDate(LocalDate birthDate) {
        this.birthDate = birthDate;
    }
    /**
     * Get the value of name
     *
     * @return the value of name
     */
    public String getName() {
        return name;
    }
    /**
     * Set the value of name
     *
     * @param name new value of name
     */
    public void setName(String name) {
        this.name = name;
    }
    /**
     * Get the value of id
     *
     * @return the value of id
     */
    public Long getId() {
        return id;
    }
    /**
     * Set the value of id
     *
     * @param id new value of id
     */
    public void setId(Long id) {
        this.id = id;
    }
    @Override
    public int hashCode() {
        int hash = 3;
        hash = 97 * hash + Objects.hashCode(this.id);
        return hash;
    }
    @Override
    public boolean equals(Object that) {
        if (this == that) {
            return true;
        }
        if (that == null) {
            return false;
        }
        if (getClass() != that.getClass()) {
            return false;
        }
        final Pet other = (Pet) that;
        // because I am using a database generated ID, I need to explicitly check entity id's for null
        // if null, they should not be compared
        if ((this.id == null) || (other.id == null)) {
            return false;
        }
        if (!Objects.equals(this.id, other.id)) {
            return false;
        }
        return true;
    }
    public PetType getType() {
        return type;
    }
    public void setType(PetType type) {
        this.type = type;
    }
    @Override
    public String toString() {
        return "Pet{" + "id=" + id + ", name=" + name + ", birthDate=" + birthDate + ", age=" + age + ", type=" + type + '}';
    }
}

# Tables

@Table(name = "pet")
@Table (Goncalves Ex01)
  • name
  • schema
  • uniqueConstraints{}
@SecondaryTable (Goncalves Ex02)
Up until now, an Entity was a table. Sometimes, existing data models spread the data across multiple tables.

McrU5lHDWqdQbez

# equals and hashCode

@Override
public int hashCode() {
    int hash = 3;
    hash = 97 * hash + Objects.hashCode(this.id);
    return hash;
}
@Override
public boolean equals(Object that) {
    if (this == that) {
        return true;
    }
    if (that == null) {
        return false;
    }
    if (getClass() != that.getClass()) {
        return false;
    }
    final Pet other = (Pet) that;
    // because I am using a database generated ID, I need to explicitly check entity id's for null
    // if null, they should not be compared
    if ((this.id == null) || (other.id == null)) {
        return false;
    }
    if (!Objects.equals(this.id, other.id)) {
        return false;
    }
    return true;
}
  • Review Object as a Superclass in Java SE Tutorial
    在 Java SE 教程中将对象作为超级类进行审核
    • Also: IBM Hashing it Out
    • Also: Understanding Inheritance in Java 了解 Java 的继承
  • If you override equals , you must override hashCode
    如果你覆盖,你必须覆盖哈希代码
equals
compares two objects, returns true if equal 比较两个对象,返回真实,如果相等
hashCode
If two objects are equal, their hash codes must also be equal 如果两个对象是相等的,则它们的哈希代码也必须相等
  • By default, returns the memory address 默认情况下,返回内存地址
  • Overriding equals invalidates default implementation
    覆盖 equals 使默认操作无效

# Primary Keys 主键

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
  • Uniquely identify each row in a table 表中每一行的唯一识别
  • JPA requires entities to have an identifier mapped to a primary key
    JPA 要求实体将标识符映射到主密钥
  • Comprises either single or set of columns 包括单列或单列集
    • Simple Primary Key 单个主键
    • Composite Primary Key 复合主键
  • If you can, stick to a simple key 如果可以,坚持一个简单的键
  • Natural ID vs Generated (Surrogate) ID 自然 ID vs 生成 (代理) ID

# Simple Primary Keys

@Id (Goncalves Ex01 and Ex02)
  • Must correspond to single attribute of entity
    必须对应实体的单一属性
  • Must not change 不能改变
  • Allowable types: 允许的类型
    • Primitive Java types 原始 java 类型: byte , int , short , long , char
    • Wrapper classes of primitive Java types 原始类型的包裹类型: Byte , Integer , Short , Long , Character
    • Arrays of primitive or wrapper types 数组: int[] , Integer[]
    • Strings, numbers, and dates: String , BigInteger , Date
Id Generation for Simple PK
  • @Id value can be created manually by application or automatically by the provider
  • @GeneratedValue (Goncalves Ex01) has different strategies
    • SEQUENCE
    • IDENTITY
    • TABLE
    • AUTO
# Option One - Natural ID/Business Key

自然身份证 / 公司密钥

So sayeth Vlad:

  • “Those entity fields having the property of being unique in the whole entity object space are generally called a business key.”
    那些在整个实体对象空间中具有唯一性的实体字段通常称为业务密钥。

  • “The business key is also independent of any persistence technology employed in our project architecture, as opposed to a synthetic database auto incremented id.”
    业务密钥还独立于我们的项目体系结构中采用的任何持久性技术,与合成数据库自动递增的 ID 相对。

  • “So, the business key must be set from the very moment we are creating the Entity and then never change it.”
    因此,必须从我们创建实体的那一刻起就设置业务密钥,然后再也不要更改它。

  • Decide/Design what attribute(s) determine uniqueness in each of your entities
    决定 / 设计哪些属性决定每个实体的唯一性
    • ISBN
    • UPC Code
    • Person ID Number
    • Drivers License Number
    • Library Card Number
    • Serial Number
    • First, Last, Middle Name, Sex at Birth, Date of Birth, and Instant of Birth
    • SSN
    • Some Made Up ID Number
    • Etc
  • These should be unique in your database - whether PK or not
    这些在您的数据库中应该是唯一的 —— 无论是否为 PK
  • These unique identifiers are generated by your application
    这些唯一标识符由您的应用程序生成
  • Use these attribute(s) for your equals and hashCode
    将这些属性用于您的 equals 和 hashCode
# Option Two – Generated ID
  • You can use the database @GeneratedID in
    equals / hashCode provided that
    • You consider null in your equals method, and return false if one of the entity identifiers is null
      equals 方法中为 null,并且如果实体标识符之一为 null,则返回 false
    • hashCode always returns the same value
      hashCode 总是返回相同的值
    • https://vladmihalcea.com/how-to-implementequals-and-hashcode-using-the-jpa-entityidentifier/
      (ref: Fixing the entity identifier equals and hashCode)
# Option Three – UUID
  • If you can’t figure out a business key, and you don’t want to use the database @GeneratedID , you can use UUID:
    • Database agnostic 数据库不可知
    • Can be generated by application 可以由应用程序生成
    • https://vladmihalcea.com/uuid-identifier-jpahibernate/ (ref: GenerationType.AUTO )

This strategy may need some adjustment for use with EclipseLink, but the fundamentals here are good. @NaturalID is Spring only – don’t use

# Composite Primary Keys 复合主键

  • When it is not possible to use a single column or attribute, JPA supports compound or composite
    当不可能使用单个列或属性时,JPA 支持混合或复合
  • Two strategies, both require separate primary key class
    两种策略都需要单独的主键类
    • Must implement equals and hashCode
      必须实现 equals 和 hashCode
    • public class
    • Public accessors and mutators
    • Default noarg constructor
    • Implement serializable if they need to cross architectural layers
  • Same database output, but querying is different
    数据库输出相同,但查询不同
# Embeddables 可嵌入的
@Embeddable (Goncalves Ex29 and Ex32) @Embedded
  • Used with composite primary keys
    与复合主键一起使用
  • Embeddables do not have a persistent identity
    可嵌入对象没有持久性
  • They are embedded in owning entities
    它们嵌入在拥有的实体中
  • If the owning entity is removed, so is the embedded
    如果拥有实体被删除,则嵌入式实体也被删除
# Embedded Composite Primary Key Class
@Embeddable with @EmbeddedId (Goncalves Ex04)
  • Create @Embeddable class conforming to above requirements
  • Identify primary key class with @EmbeddedId on entity
# Nonembedded Composite Primary Key Class
@IdClass (Goncalves Ex06)
  • Each field needs to also be declared on entity and annotated with @Id
    每个字段也需要在实体上进行申报,并附上 @Id
  • Primary key class does not require annotations, but must conform to above requirements
    主要关键类不需要注释,但必须符合上述要求
  • Identify primary key class as @IdClass on entity
    将主要关键类确定为实体上的 @IdClass
# Embedded vs NonEmbedded

For Composite PK, which way is better?

What does Vlad think?
https://vladmihalcea.com/the-best-way-tomap-a-composite-primary-key-with-jpa-andhibernate/

# Attributes 属性

@NotBlank
@Column(name = "pet_name", nullable = false, unique = true)
private String name;
@PastOrPresent
private LocalDate birthDate;
@Transient
private Integer age;
@Enumerated(EnumType.STRING)
private PetType type;
@Basic (Goncalves Ex09)
  • optional (default true)
  • fetch (default FetchType.EAGER)
@Column (Goncalves Ex11)
  • Defines the properties for a column such as name, size, unique, nullable, insertable, updateable
  • Name
  • Unique (default false)
  • Nullable (default true)
  • Length (default 255)
@Temporal (Goncalves Ex14)
  • TemporalType.DATE
  • TemporalType.TIME
  • TemporalType.TIMESTAMP

No longer required with JPA 2.2 when using the java.time API (LocalDate, LocalTime, LDT)

@Transient (Goncalves Ex14)
  • A way to not map an attribute to persistent table
    一种不将属性映射到持久表的方法
  • For example, a calculated field
    例如,计算字段
@Enumerated (Goncalves Ex17) 枚举
  • Can use EnumType.STRING to store data with String as opposed to ordinal
    可以使用 EnumType.STRING 将数据存储为 String 而不是序数
  • Adding a new constant to the top of the enum would re-arrange ordinals…
    在枚举的顶部添加一个新常量将重新排列序数…
PetType.java 枚举类
public enum PetType {
    
    CANINE("Dog"),
    FELINE("Kitty"),
    REPTILE("Snake"),
    RABBIT("Bunny"),
    FISH("Fishy");
    
    private String label;
    private PetType(String label){
        this.label = label;
    }
    
    public String getLabel(){
        return label;
    }
    
}

# Access Type 访问类型

Field Access (Annotate attributes) 字段访问 (注释属性)
  • Default used in the book
  • AccessType.FIELD
@Transient
private Integer age;
Property Access (Annotate getters) 属性访问 (注释获取器)
AccessType.PROPERTY
@Transient
public Integer getAge() {
    return age;
}

Recommendation – be consistent with your choice. (Goncalves Ex20)

# JPQL

  • What about querying? Everything up until here as been EntityManager.find(pk)
  • JPQL is an object-oriented query language, in which we are looking for entities or collections of entities
    JPQL 是一种面向对象的查询语言,我们正在寻找实体或实体集合
  • Roots in SQL, but uses standard dot notation
    基于 SQL,但使用标准点符号

# Simplied JPQL Statement Syntax

SELECT <select clause>
FROM <from clause>
[WHERE <where clause>]
[ORDER BY <order by clause>]
[GROUP BY <group by clause>]
[HAVING <having clause>]

# JPQL Tools and Syntax

  • NetBeans JPQL Editor
  • Right-click Persistence Unit and choose “Run JPQL Query”
  • Goncalves Chapter 6 Ex 21

# Dynamic Queries

  • Created as needed by the application
    根据应用程序的需要创建
  • “On the fly”
  • Can accept named or positional parameters
    可以接受命名或位置参数
  • Use EntityManager.createQuery() method, passing a String representing JPQL query
Query query = em.createQuery()
TypedQuery<E> query = em.createQuery()
query.getResultList();

# Named Queries

Pet.java
@Entity
@Table(name = "pet")
@NamedQuery(name = "Pet.findPetByName", query = "select p from Pet p where p.name = :NAME")
@NamedQuery(name = "Pet.findAll", query = "select p from Pet p")
public class Pet {
···
  • Similar to PreparedStatement
  • Static and Unchangeable 静态、不可更改
    • lose flexibility 失去灵活性
  • Translated to SQL when the application starts
    程序开始时翻译成 SQL
  • gain efficiency and performance
    提高效率和绩效
    • EntityManager.createNamedQuery()
    • The names of NamedQueries must be unique within persistence context, even across entities
      NamedQueries 的名称必须在持久性环境中独一无二,即使在实体之间也是如此
Driver.java
public class Driver {
    private static final Logger LOG = Logger.getLogger(Driver.class.getName());
    public static void main(String... args) {
        Pet cat = new Pet("Fluffy", LocalDate.of(2020, Month.DECEMBER, 12), PetType.FELINE);
        Pet dog = new Pet("Spike", LocalDate.of(2020, Month.NOVEMBER, 22), PetType.CANINE);
        LOG.info("Before em.persist==========================================>");
        LOG.info(cat.toString());
        LOG.info(dog.toString());
        // reading the persistence.xml is the entry point where entities are created as tables
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("itmd4515testPU");
        EntityManager em = emf.createEntityManager();
        EntityTransaction tx = em.getTransaction();
        tx.begin();
        //
        // do some JPA work
        em.persist(cat);
        em.persist(dog);
        LOG.info("After em.persist, before tx.commit ==========================================>");
        LOG.info(cat.toString());
        LOG.info(dog.toString());
        em.flush();
        LOG.info("After em.persist, before tx.commit, after em.flush ==========================================>");
        LOG.info(cat.toString());
        LOG.info(dog.toString());
        //
        tx.commit();
        LOG.info("After em.persist, after tx.commit ==========================================>");
        LOG.info(cat.toString());
        LOG.info(dog.toString());
        Pet foundPetFromDatabase = em.find(Pet.class, 1l);
        LOG.info("Found Pet 1: " + foundPetFromDatabase.toString());
        // JPQL query example
        foundPetFromDatabase = em
                .createQuery("select p from Pet p where p.name = :NAME", Pet.class)
                .setParameter("NAME", "Spike")
                .getSingleResult();
        LOG.info("Found Spike: " + foundPetFromDatabase.toString());
        foundPetFromDatabase = em
                .createNamedQuery("Pet.findPetByName", Pet.class)
                .setParameter("NAME", "Fluffy")
                .getSingleResult();
        LOG.info("Found Fluffy: " + foundPetFromDatabase.toString());
        List<Pet> pets = new ArrayList<>();
        pets = em.createNamedQuery("Pet.findAll", Pet.class).getResultList();
        for (Pet p : pets) {
            LOG.info(p.toString());
        }
        em.close();
        emf.close();
    }
}

# Other Query APIs

  • Criteria API
    criteriaQuery.select(c).where(builder.equal(c.get("first Name"), "Vincent"));
  • Native Queries
    Allows execution of native SQL through JPA
    允许通过 JPA 执行原生 SQL
  • Stored Procedure Queries
    Allows execution of existing database SP’s through JPA
    允许通过 JPA 执行现有数据库 SP's
  • Query By Example (QBE or Matcher)
  • QueryDSL

# Lab

# Summary

The purpose of this assignment is to learn the basics of ORM and EntityManager operations, demonstrate through JUnit test cases, and to begin the design process for your final project.
这项作业的目的是学习 ORM 和 EntityManager 操作的基础知识,通过 JUnit 测试用例进行演示,并开始最终项目的设计过程。

# Requirements

# Database Setup

Use your itmd4515 database and user from Lab 2 - Setup and Introductory Webapp.

# Project Setup

Your uid-fp repository should already be setup, and you should continue pushing your commits into GitHub for this lab.

Deviating from the package convention given above will mean that you can not benefit from Sonar and other automated tools, and I will not be able to fix this. Please follow the specification!

We will be working in this repository from now until the end of the semester.  Please remember, I will be looking for multiple commits.  Use a prefix of Lab 5 on your commit messages, for example:

  • Lab 5 - Initial Commit
  • Lab 5 - Initial entity
  • Lab 5 - Test cases

# Project Requirements

  1. First, and most importantly, think about what kind of business domain you would like to work with for the rest of the semester.  There are many examples you could pick from, but you will do your best work if you are engaged in the entities and concepts that underly your code.  If you are stuck and need examples, post a question to Confluence.  If you pick your domain now, then you will be able to continue building on this code for future labs and projects, thereby making your efforts cumulative.  Some example ideas include:
    首先,也是最重要的一点,请考虑在本学期的剩余时间里要使用哪种业务领域。您可以从许多示例中进行选择,但是如果您参与代码背后的实体和概念,则将尽力而为。如果您陷入困境并需要示例,请向 Confluence 发表问题。如果您现在选择域,那么您将能够继续在此代码上构建以用于将来的实验室和项目,从而使您的工作不断积累。一些示例想法包括:

    1. Library
    2. Education/Learning (like Blackboard)
    3. Sports
    4. Music
    5. Retail
    6. Hospitality
    7. Medical/Insurance
    8. Inventory or other ERP function
  2. Move your Lab 4 - Web Applications, Servlet and JSP classes into a separate package.  I named mine Lab 4.  We'll clean these up completely and remove them in a future lab.

  3. Create a RESOURCE_LOCAL Persistence Unit named itmd4515testPU connecting to your itmd4515 database using the itmd4515 user.  Use the persistence.xml configuration we discussed in class to drop-and-create the tables.
    创建一个名为 itmd4515testPU 的 RESOURCE_LOCAL 持久性单元,使用 itmd4515 用户连接到 itmd4515 数据库。使用我们在课堂上讨论过的 persistence.xml 配置来拖放表。

    +++code

    <?xml version="1.0" encoding="UTF-8"?>
    <persistence version="2.2" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd">
    <!-- Define Persistence Unit -->
    <persistence-unit name="itmd4515testPU" transaction-type="RESOURCE_LOCAL">
        <exclude-unlisted-classes>false</exclude-unlisted-classes>
        <properties>
        <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/itmd4515?zeroDateTimeBehavior=CONVERT_TO_NULL"/>
        <property name="javax.persistence.jdbc.user" value="itmd4515"/>
        <property name="javax.persistence.jdbc.driver" value="com.mysql.cj.jdbc.Driver"/>
        <property name="javax.persistence.jdbc.password" value="itmd4515"/>
        <property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/>
        </properties>
    </persistence-unit>
    </persistence>

    +++

  4. Add the following dependencies to your  pom.xml :

    1. org.eclipse.persistence.jpa 2.7.6

    2. junit (latest non-beta version of junit 5)

    3. Bean Validation (as we did in Lab 3 - Junit, JDBC and Bean Validation)

    4. mysql-connector-java (latest non-beta version of 8)

      Use appropriate scope for all your dependencies. Why are these dependencies required if we already have the javaee-api in our pom.xml?
      对所有依赖项使用适当的范围。如果我们在 pom.xml 中已经有了 javaee-api,为什么需要这些依赖关系?

      Make sure your group, artifact and version look like this, and that you include the build and plugins section as per Lab 3 - Junit, JDBC and Bean ValidationRemember, when you use NetBeans to generate persistence related configuration or code files (such as I did with the Persistence Unit) NetBeans adds additional dependencies to your pom.xml for you.  Clean it up and make sure yours looks like this:

      <dependency>
          <groupId>org.eclipse.persistence</groupId>
          <artifactId>org.eclipse.persistence.jpa</artifactId>
          <version>2.7.7</version>
          <scope>test</scope>
      </dependency>
      <dependency>
          <groupId>org.junit.jupiter</groupId>
          <artifactId>junit-jupiter-engine</artifactId>
          <version>5.7.0</version>
          <scope>test</scope>
      </dependency>
      <dependency>
          <groupId>mysql</groupId>
          <artifactId>mysql-connector-java</artifactId>
          <version>8.0.23</version>
          <scope>test</scope>
      </dependency>
      <dependency>
          <groupId>org.hibernate</groupId>
          <artifactId>hibernate-validator</artifactId>
          <version>7.0.0.Final</version>
          <scope>test</scope>
      </dependency>
      <dependency>
          <groupId>org.glassfish</groupId>
          <artifactId>javax.el</artifactId>
          <version>4.0.0</version>
          <scope>test</scope>
      </dependency>
      <dependency>
          <groupId>org.hibernate</groupId>
          <artifactId>hibernate-validator-cdi</artifactId>
          <version>7.0.0.Final</version>
          <scope>test</scope>
      </dependency>
      ...
      <plugins>
          <plugin>
              <groupId>org.apache.maven.plugins</groupId>
              <artifactId>maven-surefire-plugin</artifactId>
              <version>2.22.2</version>
          </plugin>
          <plugin>
              <groupId>org.apache.maven.plugins</groupId>
              <artifactId>maven-failsafe-plugin</artifactId>
              <version>2.22.2</version>
          </plugin>
      ...
  5. Create an Entity as demonstrated in class.  This should be a central and important concept to your business domain.  Do not implement User, Group or Role (in the context of security).  We will do that in a later lab.  Focus on the business or enterprise aspects of your domain.
    创建一个实体,如在课堂上演示的。对于您的业务领域,这应该是一个重要的重要概念。 请勿实施用户角色(在安全方面)。我们将在以后的实验室中进行。专注于域的 “业务” 或 “企业” 方面。

    1. Use an appropriate PK strategy, and use an appropriate data type for your PK (as per the options discussed in class)
      使用适当的 PK 策略,并为您的 PK 使用适当的数据类型(根据课程中讨论的选项)

    2. Include appropriate equals and hashCode methods for your PK strategy
      为您的 PK 策略包括适当的 equals 和 hashCode 方法

    3. Include at least one temporal data type
      包括至少一种时间数据类型

    4. Include at least three different data types.  There is no limit to the number of attributes you can include.  Your attributes should be sufficient to represent your entity.  Exercise good design and judgment.
      至少包括三种不同的数据类型。您可以包含的属性数量没有限制。您的属性应足以代表您的实体。进行良好的设计和判断。

    5. Include appropriate Constructors, accessors and mutators
      包括适当的构造函数,访问器和修改器

    6. Include appropriate bean validation constraints based on your database types and sizes
      根据数据库类型和大小包括适当的 bean 验证约束

    7. Include appropriate toString method for formatted output
      包括适当的 toString 方法以格式化输出

  6. Document the following on your wiki page:

    1. Paragraph that describes the business domain you have chosen to work with, and why.
      描述您选择使用的业务领域的段落,以及原因。
    2. Write a second paragraph answering the following questions: There is only one entity required for Lab 5, but what other entities from your business domain can you think of?  How might they relate to one another?  You can answer this in narrative form, or you can answer it with a database diagram.  One of your midterm questions will be very similar, about the design of your FP, so this is to help get you started early.
      写第二段回答以下问题:实验 5 仅需要一个实体,但是您能想到您业务领域中的其他哪个实体?它们可能如何相互联系?您可以以叙述形式回答此问题,也可以使用数据库图来回答。关于 FP 的设计,您的期中问题之一将非常相似,因此这有助于您早日入门。
  7. Create a JUnit test class as demonstrated in class
    创建一个 JUnit 测试类,如在类中演示的

    For additional examples (beyond my in-class demonstration) you can refer to Goncalves sample code Chapter 06 - JPA Managing and Querying ex03.
    有关其他示例(除了我的课堂演示之外),您可以参考 Goncalves 示例代码第 06 章 - JPA 管理和查询 ex03。

    1. You must use  EntityManagerFactory  to create an  EntityManager .  You may not use JDBC on this lab.
    2. Make appropriate use of JUnit test fixtures as discussed in class, to obtain an EntityManager and also to stage consistent test data for your assertions
      适当使用课堂上讨论的 JUnit 测试夹具,获取实体管理器,并为您的断言提供一致的测试数据
    3. Include test methods for CRUD functionality
      1. Create - em.persist
      2. Read - em.find and print to standard output
      3. Update - entity mutators (set methods) within a transaction
      4. Delete - em.remove
    4. Use at least one assertion in each test method to check pass/fail
      在每个测试方法中使用至少一个断言来检查通过 / 失败
    5. You must implement all test cases for your entity class
      您必须为实体类实施所有测试案例
    code
    AbstractJPATest.java
    public abstract class AbstractJPATest {
        private static final Logger LOG = Logger.getLogger(AbstractJPATest.class.getName());
        
        private static EntityManagerFactory emf;
        protected EntityManager em;
        protected EntityTransaction tx;
        
        @BeforeAll
        public static void beforeAll(){
            emf = Persistence.createEntityManagerFactory("itmd4515testPU");
        }
        
        @BeforeEach
        public void beforeEach(){
            em = emf.createEntityManager();
            tx = em.getTransaction();
            
            // just like our JDBC test cases - let's stage some consistent test
            // data to work with across our test classes
            Pet test = new Pet("TESTPET", LocalDate.of(2020, Month.DECEMBER, 12), PetType.FELINE);
            tx.begin();
            em.persist(test);
            tx.commit();
            
            LOG.info("beforeEach\t" + test.toString());
        }
        
        @AfterEach
        public void afterEach(){
            // just like our JDBC test cases - let's clean up our test data
            // after each test case
            Pet test = em
                    .createNamedQuery("Pet.findPetByName", Pet.class)
                    .setParameter("NAME", "TESTPET")
                    .getSingleResult();
            
            tx.begin();
            em.remove(test);
            tx.commit();
            
            em.close();
        }
        
        @AfterAll
        public static void afterAll(){
            emf.close();
        }
        
    }
    PetJPATest.java
    public class PetJPATest extends AbstractJPATest {
        // example "sunny day" test - expecting success
        @Test
        public void testCreateShouldPass() {
            // create an entity
            Pet cat = new Pet("Fluffy", LocalDate.of(2020, Month.DECEMBER, 12), PetType.FELINE);
            tx.begin();
            em.persist(cat);
            tx.commit();
            // assert success
            assertNotNull(cat.getId());
            assertTrue(cat.getId() >= 1l);
            // clean up after yourself
            tx.begin();
            em.remove(cat);
            tx.commit();
        }
        // example "rainy day" test - expecting failure
        @Test
        public void testCreateShouldFail() {
            // create an entity
            Pet cat = new Pet("TESTPET", LocalDate.of(2020, Month.DECEMBER, 12), PetType.FELINE);
            assertThrows(RollbackException.class, () -> {
                tx.begin();
                em.persist(cat);
                tx.commit();
            });
        }
        @Test
        public void testRead() {
            // find a pet from the database
            Pet test = em
                    .createNamedQuery("Pet.findPetByName", Pet.class)
                    .setParameter("NAME", "TESTPET")
                    .getSingleResult();
            
            assertNotNull(test);
            assertEquals("TESTPET", test.getName());
        }
        @Test
        public void testUpdate() {
            // find a pet from the database
            Pet test = em
                    .createNamedQuery("Pet.findPetByName", Pet.class)
                    .setParameter("NAME", "TESTPET")
                    .getSingleResult();
            // update something about this, but be careful - the afterEach em.remove
            // still needs to work.  In my case - that means watch out for the name
            tx.begin();
            test.setType(PetType.FISH);
            tx.commit();
            
            // next, read it back from the database and assert your update was OK
            test = em.find(Pet.class, test.getId());
            assertEquals(PetType.FISH, test.getType());
        }
        @Test
        public void testDelete() {
            // create an entity
            Pet cat = new Pet("Fluffy", LocalDate.of(2020, Month.DECEMBER, 12), PetType.FELINE);
            tx.begin();
            em.persist(cat);
            tx.commit();
            // assert created OK
            assertNotNull(cat.getId());
            
            // remove it
            tx.begin();
            em.remove(cat);
            tx.commit();
            
            // assert it was removed OK by trying to re-find it from the database
            cat = em.find(Pet.class, cat.getId());
            assertNull(cat);
        }
    }
  8. Document in your wiki page by adding code block(s) containing the output of your Test class.  Clearly identify each operation and discuss what is being tested.
    添加包含测试类输出的代码块到文档。清楚地识别每个操作并讨论正在测试的内容。

  9. Submit to Blackboard

    1. Right your uid-fp project and select "Clean"
    2. Go to your NetBeans Projects directory.  Create a zip file of the uid-fp folder and submit it to the Blackboard assignment.

# Extra Credit (5 Points)

  1. Include a second JUnit test class for bean validation.  Do not mix JPA and Validation test cases, keep them within their respective test class.
    包括第二个 JUnit 测试类以进行 bean 验证。 请勿混用 JPA 和 Validation 测试用例,请将它们放在各自的测试类中。
  2. Use abstraction to keep generic functionality and test fixtures at a more re-usable level in your test cases.
    使用抽象可将通用功能和测试装置保持在测试用例中更可重用的级别。
  3. Include pass/fail (sunny-day/rainy-day) test cases for each of your validation constraints.
    为每个验证约束包括通过 / 失败(晴天 / 阴雨天)测试用例。