A data is a piece or collection of information. By itself it might not mean anything concrete, and needs to be interpreted to become information.
A database is an organized collection of data.
This information is typically stored electronically in a computer system.
Currently, a computer database is usually controlled by a database management system (DBMS), and the data is written and queried using a structured query language (SQL).
The database and the DBMS software are usually referred to as the database system. These systems allow users to store, maintain and manipulate data.
Files and navigational databases such as the hierarchical database were the original systems used to store and manipulate data.
Although simple, these early systems were inflexible, so new systems arose which allowed for more specifications, such as database structure data relationships.
In the 80's, these relational databases became popular.
More recently, NoSQL databases appeared as a response to the need for faster speed and processing of unstructured data brought on by the popularization of the Internet.
It isn't uncommon for larger systems to use different types of data storage and database systems, in order to solve different problems with more effective solutions.
Database administrators face a few common challenges:
A database model determines the design and structure of a database. Through the years, different models emerged, such as:
Let's take a closer look at some specific types of databases.
A flat file is also known as a text database. It basically consists of a database that stores data in plain text format. These files usually contain one record per line, and are often created in spreadsheet format
Relationships between entries are not explicitly defined in this type of database: it must be interpreted by a user.
It is still widely used in different formats, as it is quite simple and easy to use. Flat-file databases can be implemented using regular text files or spreadsheets (e.g., comma-separated values).
In the relational model, data is organized into tables, composed of rows and columns. Tables usually represent a type of entity, also called 'relation' (e.g., "User").
Each column corresponds to an attribute (e.g., "name"). Each row corresponds to an entry or tuple, with values for each attribute.
One or more columns can be defined as primary keys, that is, their value(s) could be used to uniquely identify a row or tuple.
These keys can then be used to make cross-references between tables, which establish relationships.
The values within each table are related to each other.
Tables may also be related to other tables.
Queries may be run across multiple tables at once.
A relational database structures data according to the relational model.
A database schema represents the definition and structure of a relational database, that is, its tables, columns, relationships, and all other elements.
A relational database can be managed and accessed using queries, through database management systems.
A DBMS is a software system that allows users to manage databases, more specifically:
A RDBMS is a DBMS specifically used for relational databases.
The need for usage of a DBMS should always be carefully evaluated.
Non-relational databases, often called NoSQL databases, store their data in a non-tabular form.
NoSQL databases come in a variety of types based on their data model.
NoSQL databases provide flexible schemas and scale easily with large amounts of data and high user loads.
For the rest of this lesson, we will mostly focus on RDBMS.
MySQL is:
In 2021, MySQL remains the most popular Open Source SQL database management system.
Installing and Configuring MySQL Server
Server Tutorial
// DATABASE CONNECTION
$ mysql -h host -u user -p
// HELP
$ mysql --help
CONSTRAINT | DEFINITION |
---|---|
NOT NULL | Column cannot have null value |
UNIQUE | All values in column are different |
PRIMARY KEY | Combination of NOT NULL and UNIQUE. Uniquely identifies each row in a table. |
FOREIGN KEY | Represents a relationship between tables |
CHECK | Ensures that values in a column satisfy a specific condition |
DEFAULT | Sets a default value if none is specified |
CREATE INDEX | Used to retrieve data from the database more quickly |
Every table must have a primary key.
This constraint uniquely identifies a table record.
class_name | credits | classroom | instructor |
---|---|---|---|
Geometry | 5 | 306 | Georgia Miller |
Advanced Calculus | 6 | 301 | Georgia Miller |
Intro to Biology | 4 | 213 | Paul Stevens |
Computer Science | 5 | 418 | Sarah Hughes |
Political Science | 5 | 101 | Marty Greene |
What would be a good primary key here?
book_name | book edition | author | available |
---|---|---|---|
A Song Of Ice And Fire | 2 | George R. R. Martin | true |
Circe | 1 | Madeline Miller | true |
Ham On Rye | 5 | Charles Bukowski | false |
Darkmans | 4 | Nicola Barker | false |
Ham On Rye | 10 | Charles Bukowski | true |
What about here?
In the previous example there was no single attribute for a primary key.
In cases like this, we can follow one of two paths:
Composite primary keys are composed of several attributes.
In the example above, assuming there would only be one copy of each book in a particular edition, the appropriate primary key would be composed of book_name and edition.
book_name | book edition | author | available |
---|---|---|---|
A Song Of Ice And Fire | 2 | George R. R. Martin | true |
Circe | 1 | Madeline Miller | true |
Ham On Rye | 5 | Charles Bukowski | false |
Darkmans | 4 | Nicola Barker | false |
Ham On Rye | 10 | Charles Bukowski | true |
// TABLE CREATION
CREATE TABLE my_table(
column1 DATATYPE [ CONSTRAINT ] ,
column2 DATATYPE [ CONSTRAINT ] ,
PRIMARY KEY (column1, column 2)
);
// OR
CREATE TABLE my_table(
column1 DATATYPE [ CONSTRAINT | PRIMARY KEY ] ,
column2 DATATYPE [ CONSTRAINT ]
);
The Car-Rental Database
(Table creation)
A foreign key is an attribute that creates a relationship between two tables.
Their purpose is to maintain data integrity and allow navigation between two different instances of an entity
dept_code | dept_name |
---|---|
001 | Science |
002 | English |
003 | Computer |
004 | History |
teacher_id | first_name | last_name |
---|---|---|
0923 | David | Lee |
1423 | Grace | Mason |
2390 | Thea | Fowler |
2483 | Zack | Brenton |
How would you relate these two tables?
In a relational database, a relationship is formed by correlating rows belonging to different tables. This relationship is also known as cardinality.
There three 3 types of relationships:
The most common relationship.
It associates a row from a parent (one) table to multiple rows in a child (many) table.
Associates one row from on table to a single row on the other table. One table serves as the parent table, the other one as the child.
This is a special kind of relationship because it is the only one in which both tables may share the same primary key.
Associates multiple rows from the parent table to multiple rows from the child table.
Requires a linking-table containing two foreign key columns that reference the two different parent tables.
// TABLE CREATION
CREATE TABLE my_table(
column1 DATATYPE [ CONSTRAINT ] ,
column2 DATATYPE [ CONSTRAINT ]
PRIMARY KEY (column1),
FOREIGN KEY (column2) REFERENCES mytable2(column1)
);
// OR
CREATE TABLE my_table(
column1 DATATYPE [ CONSTRAINT | PRIMARY KEY ] ,
column2 DATATYPE [ CONSTRAINT | FOREIGN KEY (column2) REFERENCES mytable2(column1)]
);
A database schema is the collection of table schemas for the whole database.
Some programs like MySQL Workbench allow you to visually design, model, generate, and manage databases.
The Car-Rental Database
(Establishing relationships)
In this exercise you should:
SQL is a programming language used to query, manipulate, and define data in databases.
Major commands include:
When adding values for all the columns of the table, we don't need to specify the column names.
INSERT INTO mytable(column1, column2)
VALUES (column1_value, column2_value);
INSERT INTO mytable
VALUES (column1_value, column2_value);
Selecting all attributes from a table.
// SELECTING EACH COLUMN
SELECT column1, column2 FROM mytable;
// SELECTING ALL COLUMNS
SELECT * FROM mytable;
SELECT column1 FROM mytable;
Selecting one attribute from a table.
Selecting all attributes from a table.
// SELECTING EACH COLUMN
SELECT column1, column2 FROM mytable;
// SELECTING ALL COLUMNS
SELECT * FROM mytable;
SELECT column1 FROM mytable;
Selecting one attribute from a table.
UPDATE table
SET column1=column1_value
WHERE column2=column2_value;
ALTER TABLE mytable
ADD column3 DATATYPE [ CONSTRAINTS ]
Updating the table schema is a little bit different:
Delete specific records:
DELETE FROM mytable WHERE column=column_value;
Delete all data from a table:
DELETE FROM mytable;
Delete the table itself:
DROP TABLE mytable
Delete the database itself:
DROP DATABASE mydatabase
The Car-Rental Database
(Populating the Database)
In this exercise you should:
Select queries always return a result-set.
The beauty of select queries is in the conditional clauses.
SELECT * FROM mytable
WHERE { condition };
The where clause can also be used in update and delete statements.
OPERATOR | DESCRIPTION |
---|---|
= | Equal |
>, >= | Greater than (or equal) |
<, <= | Less than (or equal) |
!= | Not equal |
BETWEEN | Between a certain range |
LIKE | Search for a pattern |
IN | Specify multiple possible values |
You can also use AND, OR, and NOT operators.
The Car-Rental Database
(Query the Database)
In this exercise you should:
SELECT COUNT(column_name) FROM mytable
WHERE { condition };
COUNT() returns the number of rows that match the specified criteria.
SELECT AVG(column_name) FROM mytable;
AVG() returns the average value of a numeric column.
SELECT SUM(column_name) FROM mytable;
SUM() returns the total sum of a numeric column.
MIN() returns the smallest value of the selected column.
SELECT MAX(column_name) FROM mytable;
MIN() returns the highest value of the selected column.
SELECT MIN(column_name) FROM mytable;
SELECT column1, column2 FROM mytable
ORDER BY column1, column2 { ASC|DESC };
Used to sort the result-set in ascending or descending order.
SELECT column_name AS alias_name FROM mytable;
Used to give a table, or a column in a table, a temporary name.
An alias only exists for the duration of that query.
A join clause is used to combine rows from two or more tables, based on a related column between them.
There are 4 different types of joins:
SELECT column_name
FROM mytable1
INNER JOIN mytable2
ON mytable1.column_name = mytable2.column_name;
// A REAL EXAMPLE:
SELECT orders.order_id, customers.customer_name
FROM orders
INNER JOIN customers ON orders.customer_id = customers.customer_id;
As a result of the query above, we will have a table with the order IDs related to the customers names.
SELECT DISTINCT column_name
FROM mytable1
WHERE { condition }
GROUP BY { grouping-columns }
HAVING { condition }
DISTINCT - used to return only different (distinct) values
Example query: "Find all the countries in the database"
GROUP BY - groups rows that have the same values
Example query: "Find the number of customers in each country"
HAVING - Functions as WHERE, except WHERE can't be used with aggregate functions.
WITH temporarytable AS (
SELECT DISTINCT column_name
FROM mytable1)
SELECT * FROM temporarytable
LIMIT { number }
WITH - Lets us store the result of a query in a temporary table, using an alias.
LIMIT - Lets us specify the maximum number of rows of the result set
The Car-Rental Database
(Query the database... Again.)
In this exercise you should:
A batch of SQL statements that you can group as a logical unit, so they can be reused over and over again. Stored procedures are useful when whenever we have a series of SQL statements that we often repeat.
DELIMITER //
CREATE PROCEDURE myProcedure
( { statements } );
END //
DELIMITER ;
Stored procedures are easily modified, and fairly secure and performant.
However, they are often platform-specific, and separate the data access logic from the application itself.
A special type of stored procedure that automatically runs when a certain event occurs in the database server.
DELIMITER //
CREATE TRIGGER mytrigger
{BEFORE / AFTER / INSTEAD OF } { action } ON mytable
FOR EACH ROW
BEGIN
{ statement }
END //
DELIMITER ;
A view is a virtual table based on the result set of an SQL statement.
The CREATE VIEW statement works very similar to the WITH clause, except the first actually creates an object in the database.
CREATE VIEW myview AS
SELECT { columns }
FROM mytable1
WHERE { condition };
SECURITY - Views can be made accessible to users while the underlying tables are not directly accessible.
SIMPLICITY - Views can be used to hide and reuse complex queries.
Indexes are used to retrieve data from the database more quickly.
CREATE INDEX myindex
ON mytable1 { columns };
An index helps to speed up SELECT queries and WHERE clauses, but it slows down data input.
Database design is a set of steps that help with designing, creating, implementing and maintaining it.
A well-designed database contains accurate and up-to-date information, allowing data to be fetched easily whenever needed.
A set of rules designed to organize data in a database in a way that protects the data and, at the same time, makes the database more flexible by eliminating redundancy and inconsistent dependency.
Each rule is called a "normal form".
If the database is designed in a way that respects the first rule, we say it's in the first normal form.
Third normal form is considered a satisfactory database design for most applications.
1. An attribute on a table should not hold multiple values
2. Each row should be uniquely identified by a primary key
student_id | first_name | last_name | languages |
---|---|---|---|
2340 | Ursula | Granger | Java, C++ |
2431 | Dean | Cooper | PHP, Python |
2478 | Archie | Mueller | Java |
Is this table in the 1NF?
student_id | first_name | last_name | languages |
---|---|---|---|
2340 | Ursula | Granger | Java |
2340 | Ursula | Granger | C++ |
2431 | Dean | Cooper | PHP |
2431 | Dean | Cooper | Python |
2478 | Archie | Mueller | Java |
1. It's in the 1NF
2. Every non-key attribute is functionally dependent on the Primary Key.
Is this table in the 2NF?
student_id | first_name | last_name | languages |
---|---|---|---|
2340 | Ursula | Granger | Java |
2340 | Ursula | Granger | C++ |
2431 | Dean | Cooper | PHP |
2431 | Dean | Cooper | Python |
2478 | Archie | Mueller | Java |
student_id | first_name | last_name |
---|---|---|
2340 | Ursula | Granger |
2340 | Ursula | Granger |
2431 | Dean | Cooper |
2431 | Dean | Cooper |
2478 | Archie | Mueller |
student_id | languages |
---|---|
2340 | Java |
2340 | C++ |
2431 | PHP |
2431 | Python |
2478 | Java |
1. It's in the 2NF
2. Transitive dependencies of non-key attributes on any key should be removed.
Are these tables in the 3NF?
student_id | first_name | last_name |
---|---|---|
2340 | Ursula | Granger |
2340 | Ursula | Granger |
2431 | Dean | Cooper |
2431 | Dean | Cooper |
2478 | Archie | Mueller |
student_id | languages |
---|---|
2340 | Java |
2340 | C++ |
2431 | PHP |
2431 | Python |
2478 | Java |
Maybe; there is still a little bit of repetition in our tables.
But is the current design sufficient for what we need to do? If it is, there's no need to continue normalising our database.
The MindSwap Database
We need a database to save data regarding the MindSwap students. Typical queries done to this database include:
Database concurrency is the ability of a database to allow multiple users to affect multiple transactions.
The ability to offer concurrency is unique to databases, compared to other forms of data storage, like files.
As we know, with concurrency comes efficiency but also problems...
A transaction is a logical, atomic unit of work that contains one or more SQL statements.
Each transaction performs in an all-or-nothing proposition: either each statement completes its execution, or none will have any effect.
All database transactions obey the ACID properties.
ACID is an acronym for the following:
All tasks of a transaction are performed or none of them are. There are no partial transactions.
Why do transactions fail?
The transaction takes the database from one consistent state to another consistent state
Any data that is written to the database must be validated according to all restrictions (keys, foreign keys, cascades, triggers, etc.). Note that this property isn't related to the correctness of your application's logic.
Example: in a banking transaction that debits an account and credits another account, a failure must not cause the database to alter only one account.
The effect of a transaction is not visible to other transactions until the transaction is committed.
Example: one user updating a table does not see the uncommitted changes to that table made concurrently by another user.
Changes made by committed transactions are permanent.
After a transaction completes, the database ensures through its recovery mechanisms that changes from the transaction are not lost. Changes are kept in a log.
The pessimistic locking mechanism is provided by the RDBMS itself. It locks the record while it's being read/updated.
Locks are released when the transaction is committed/rolled back.
Pessimistic locking doesn't allow for much concurrency.
The solution is to use an optimistic locking mechanism. This mechanism involves the introduction of a version attribute.
Database Transactions
The way that object-oriented languages deal with data is fundamentally different from the way data is dealt with in the relational world.
This leads to some mismatch problems.
Loading and storing graphs of objects using a tabular relational database exposes us to 5 mismatch problems:
Granularity refers to the extent to which a system can be broken down into smaller parts.
Sometimes you will have an object model which has more classes than the number of corresponding tables in the database.
Inheritance is natural in object-oriented programming languages. However, RDBMSs do not define anything similar.
A RDBMS defines one notion of equality: the primary key.
In Java, we have both object identity (a==b) and object equality (a.equals(b)).
Associations are represented as unidirectional references in object-oriented languages, whereas RDBMSs use the notion of foreign keys.
If bidirectional relationships are needed in Java, we must define the association twice.
Foreign key associations, however, are bidirectional in themselves.
The way we access objects in Java and in RDBMS is very different.
In Java , we navigate from one association to another walking through the object graph. This is not an efficient way of retrieving data from a relational database.
Minimizing SQL queries is crucial to a good performance. If we want to get data from more than one table, we write a SQL join query.
We've taken a look at the problems a developer faces when it's time to connect a database system to a Java application, but... How do we solve them?
There are a few software pieces that help us with this task.
JDBC is a Java API for database connectivity. JDBC allows us to:
All these actions are possible with the use of a JDBC driver to access the database.
The Driver Manager manages a list of database drivers. It matches connection requests from the Java application with the proper database driver.
The Driver handles communications with the database server.
The data source is specified by a database URL that contains information regarding the name, location, and configuration properties of the database to connect to.
public Connection getConnection() {
String dbURL = "jdbc:mysql://localhost:3306/myDatabase";
String username = "root";
String password = "";
Connection connection = null;
try {
connection = DriverManager.getConnection(dbURL, username, password);
} catch (SQLException ex) {
System.out.println("Connection failed: " + ex.getMessage());
}
return connection;
}
public void close(Connection connection) {
try {
if (connection != null) {
connection.close();
}
} catch (SQLException ex) {
System.out.println("Failure to close: " + ex.getMessage());
}
}
public List<User> getUsers() {
List<User> users = new LinkedList<>();
// CREATING A STATEMENT
Statement statement = connection.createStatement();
// CREATIING A QUERY
String query = "SELECT * FROM user";
// EXECUTING THE QUERY
ResultSet resultSet = statement.executeQuery(query);
// DEALING WITH THE RESULTS
(...)
return users;
}
public List<User> getUsers() {
// CREATING THE STATEMENT AND EXECUTING THE QUERY
(...)
// DEALING WITH THE RESULTS
while(resultSet.next()){
String firstName = resultSet.getString("firstname");
String lastName = resultSet.getString("lastname");
String email = resultSet.getString("email");
users.add(new User(firstName, lastName, email));
}
return users;
}
try {
connection.setAutoCommit(false);
Statement statement = connection.createStatement();
String withdrawQuery = "UPDATE accounts SET balance=45 WHERE id=145";
String depositQuery = "UPDATE accouns SET balance=105 WHERE id=123";
statement.executeUpdate(withdrawQuery);
statement.executeUpdate(depositQuery);
// IF NO ERRORS OCCUR, BOTH ROWS WILL BE AFFECTED
connection.commit();
} catch(SQLException e){
// IF AN ERROR OCCURS, NO ROW WILL BE AFFECTED
connection.rollback();
}
JDBC connections are, by default, in auto-commit mode; every SQL statement is committed to the database when it completes.
We can turn off auto-commit and manage our own transactions.
JDBC doesn't come without its downfalls. These include:
Because JDBC is somewhat limited, with the developer having the responsibility to manually map between objects and tables being its biggest disadvantage, a new way to connect a Java application to a database started to emerge: ORM tools.
ORM is a programming technique that creates a layer between the database and the application, helping us deal with the problems we saw earlier.
The basic idea of ORM is to map database tables to, in our case, Java classes.
One row of the database is represented as a class instance, and vice-versa.
Mapping describes the relationship between an object and the database without knowing much about how the database is structured.
BETTER SYSTEM ARCHITECTURE
ORM tools will implement effective design patterns that force you to use good programming practices in an application.
REDUCED CODING TIME
Without ORM tools, the mapping would have to be done by hand by the developers working on the application.
CACHING AND TRANSACTIONS
Most ORM tools provide support for multiple transactions, locking mechanisms and even versioning.
JPA is the Java ORM standard for accessing, storing, and managing data between a Java application and a RDBMS.
By itself, JPA is not a tool/framework; it simply defines a set of concepts that can be implement by other tools/frameworks (ex: Hibernate, for example).
The JPA layer interfaces with the application, and the JPA provider transforms object interactions into SQL statements.
Hibernate is one of the most popular ORM frameworks for Java applications.
It is a standard implementation of the JPA specification with a few additional features that are Hibernate specific.
Entity is the basic unit of persistence in JPA.
It's basically a regular class with metadata to specify ho its state maps to the data in the database tables.
We will look at JPA entities in further detail, later.
EntityManager is an interface that abstracts the physical connection to the database.
Entity managers are responsible for managing JPA entities in the persistence context ( set of entities which hold data to be persisted in some persistence storage (e.g. database))
A factory for EntityManager objects.
EntityManagerFactory is a thread-safe object, but heavy, which is why we typically have only one for database in an application.
It contains all the mapping metadata and caches generated SQL statements.
A persistence unit defines a set of all entity classes that are managed in an application.
Persistence units are defined by the persistence.xml configuration file. JPA then uses this file to create the connection and setup the required environment.
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" version="2.1">
<persistence-unit name="test">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<properties>
<property name="javax.persistence.jdbc.user" value="root"/>
<property name="javax.persistence.jdbc.password" value=""/>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/mindswap"/>
<property name="javax.persistence.jdbc.driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect"/>
<property name="hibernate.show_sql" value="true"/>
</properties>
</persistence-unit>
</persistence>
An entity is basically a POJO representing a table stored in a database. Each instance of an entity represents a row in the table.
@Entity(name="customers")
public class Customer {
// fields, getters and setters
}
The entity name defaults to the name of the class. However, we can change it by using the name parameter.
The entity name will be used in the queries done to the database.
To change the name of the table in the database itself, we should use the @Table annotation.
Entities must have a public, no-arguments constructor, as well as setters and getters for all persisted fields.
Entities should also have an id property to be used as the primary key, and can never be declared final.
@Entity(name="customers")
@Table(name="customers")
public class Customer {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
private String name;
@Transient
private String someProperty;
// getters and setters
}
Values for ids can be automatically generated by Hibernate when saving or persisting, by four different strategies:
@Entity
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Version
private Integer version;
@CreationTimestamp
private Date creationTime;
@UpdateTimestamp
private Date updateTime;
private String name;
}
An entity class instance can be in one of 4 possible states:
(...)
EntityManager em = emf.createEntityManager();
try {
// OPEN TRANSACTION - JPA REQUIRES IT IN ORDER TO TRIGGER THE SYNCHRONIZATION BETWEEN THE APPLICATION AND THE DATABASE
em.getTransaction().begin();
Customer savedCustomer = em.merge(customer);
// CLOSE THE TRANSACTION
em.getTransaction().commit();
} catch (RollbackException ex) {
em.getTransaction().rollback();
} finally {
if (em != null) {
em.close();
}
}
merge() - makes a copy of the instance managed, and returns it; future changes to the original instance will not be tracked. It can be used for INSERT or UPDATE operations.
(...)
EntityManager em = emf.createEntityManager();
try {
em.getTransaction().begin();
em.persist(customer);
customer.setEmail("johnnysinger@mail.com");
em.getTransaction().commit();
} catch (RollbackException ex) {
em.getTransaction().rollback();
} finally {
if (em != null) {
em.close();
}
}
persist() - makes the instance managed by the persistence context. This means that future changes to the original instance will be tracked. It is used for INSERT operations.
(...)
EntityManager em = emf.createEntityManager();
try {
Customer customer = em.find(Customer.class, id);
} finally {
if (em != null) {
em.close();
}
}
find() - Hits the database and returns an object that represents the database row. If no row is found, the method returns null.
Entity Persisting And Fetching
User Details Pages with Spring Boot MVC and JPA
/users → Shows a list of users names
/users/{id} → Shows the details of that particular user
@Entity
@Table(name = "customers")
public class Customer {
@Id
private Integer id;
private String name;
@Embedded
private CreditCard card;
}
In this case, it didn't make sense for CreditCard to be an entity in itself. It is instead a component.
@Embeddable
public class CreditCard {
private Integer number;
private Date expiration;
private Integer cvc;
}
There are a few different strategies when it comes to mapping inheritance to the relational world. These include:
Choosing the right strategy will depend on the problem in front of us.
@MappedSuperclass
public abstract class Vehicle {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private Integer gas;
@Version
private Integer version;
@CreationTimestamp
private Date creationTime;
@UpdateTimestamp
private Date updateTime;
}
Maps each concrete class to its own table.
@Entity
@Table(name = "cars")
public class Car extends Vehicle {
private Integer tireWear;
}
@Entity
@Table(name = "boats")
public class Boat extends Vehicle {
private String name;
}
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Vehicle {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE) // IDENTITY IS NOT ALLOWED
private Integer id;
private Integer gas;
@Version
private Integer version;
@CreationTimestamp
private Date creationTime;
@UpdateTimestamp
private Date updateTime;
}
Similar to mapped superclass, except the superclass is its own entity. This allows for the use of polymorphic queries.
@Entity
@Table(name = "cars")
public class Car extends Vehicle {
private Integer tireWear;
}
@Entity
@Table(name = "boats")
public class Boat extends Vehicle {
private String name;
}
Maps all entities to the same table.
@Entity
@Table(name = "products")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(
name = "product_type",
discriminatorType = DiscriminatorType.STRING
)
public abstract class Product {
@Id
private Integer id;
}
@Entity
@DiscriminatorValue("chair")
public class Chair extends Product {
private String color;
}
@Entity
@DiscriminatorValue("rug")
public class Rug extends Product {
private String pattern;
private String material;
}
@Entity
@Table(name="animals")
@Inheritance(strategy = InheritanceType.JOINED)
public class Animal {
@Id
private Integer id;
private String species;
}
Maps each class to its own table like the table per class, but the super class is now mapped to its own table.
id is both a primary and foreign key.
@Entity
@Table(name = "wild_animals")
public class WildAnimal extends Animal {
}
@Entity
@Table(name = "pets")
public class Pet extends Animal {
private String name;
}
An association represents the relationship between entities. It can be:
In the relational world, there is no directionality. However, as we've seen before, in Java associations can be unidirectional or bidirectional.
@Entity
@Table(name = "human")
public class Human {
@Id
private Integer id;
private String name;
@OneToOne(mappedBy = "human")
private Pet pet;
}
@Entity
@Table(name = "pet")
public class Pet {
@Id
private Integer id;
private String name;
// FOR BIDIRECTIONALITY
@OneToOne
private Human owner;
}
mappedBy - required for bidirectional associations. Tells hibernate to use the foreign key on the Pet table to define the owner.
@Entity
@Table(name = "product")
public class Product {
@Id
private Integer id;
private String name;
@ManyToOne
private Category category;
}
mappedBy - The category foreign key is used on the product table to establish the many-to-one relationship instead of a join table.
@Entity
@Table(name = "category")
public class Category {
@Id
private Integer id;
private String name;
@OneToMany(
cascade = {CascadeType.ALL},
orphanRemoval = true,
mappedBy = "category"
)
private Set<Product> products;
public void addProduct(Product product) {
products.add(product);
product.setCategory(this);
}
}
@Entity
@Table(name = "user")
public class User {
@Id
private Integer id;
private String name;
@ManyToMany(
fetch = FetchType.EAGER
)
private List<Role> roles;
}
@Entity
@Table(name = "role")
public class Role {
@Id
private Integer id;
private String name;
@ManyToMany(
mappedBy="role",
fetch = FetchType.LAZY
)
private List<User> users;
}
JPA/Hibernate support two types of fetching:
@Entity
public class Human {
@Id
private Integer id;
private String name;
@OneToOne(
cascade = {CascadeType.ALL},
orphanRemoval = true,
mappedBy = "human"
)
private Car car;
}
cascade - Propagates changes on parent entity to child entity. The most common cascade types are all, persist, merge, and remove.
(...)
String queryString = "SELECT customer FROM Customer customer WHERE customer.name = :name";
TypedQuery<Customer> query = em.createQuery(queryString, Customer.class);
query.setParameter("name", "John");
List<Customer> customers = query.getResultList();
JPQL is very similar to SQL, except it queries the entity model instead of querying tables. It also supports OOP features like inheritance, for example.
User - the entity being queried
user - identification variable, used in the rest of the query
Choose (and implement) the adequate inheritance mapping strategy for the following examples: