I'm new in Hibernate. What is the difference between
#Id
#GeneratedValue
private Integer id;
and
#Id
#GeneratedValue
#Column(name="id", unique=true, nullable=false, etc)
private Integer id;
What is the reason of defining this if we already did it creating database? Does Hibernate warn us when we break these constraints (not in this example since it's auto generated value)? Does it create table when there isn't one?
The only reason is if the column is named differently, for example in a USER table, if the id column is called USER_ID, you might want to map that to a User object, on the field id.
The unique and nullable attributes are not used, as PK are unique and non-null. Read the javadoc of #Column to understand what else you can define there.
As you mentioned, if you create your DB from scripts (it's actually a bad practice to create your production DB from the JPA annotations), the only relevant attribute is the name... the other ones are not used for #Id columns or are used to autogenerate the DDL.
A slight word of caution, sometimes the attribute nullable influences on how updates are ordered. As a good practice, I always set it to false on mandatory associations (as otherwise Hibernate might decide clear the association and set the column to null... and then do another update with the new ID.).
Related
After reading several articles, threads and making some research, now I am completely confused regarding to implementing a proper equals and hashCode method in my Spring Boot app.
For example, I have the following class:
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Entity
public class Recipe {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(nullable = false)
private String title;
#Column(length = 100)
private String description;
#Column(nullable = false)
private Integer prepTime;
#Column(nullable = false)
private Integer cookTime;
#Column(nullable = false)
private Integer servings;
#Lob
#org.hibernate.annotations.Type(type = "org.hibernate.type.TextType")
#Column(nullable = false)
private String instructions;
#Column(nullable = false)
#Enumerated(value = EnumType.STRING)
private Difficulty difficulty;
#Column(nullable = false)
#Enumerated(value = EnumType.STRING)
private HealthLabel healthLabel;
#ManyToOne(optional = true, fetch = FetchType.LAZY)
#JoinColumn(name = "category_id", referencedColumnName = "id")
private Category category;
#OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL, orphanRemoval = true)
private List<RecipeIngredient> recipeIngredients = new ArrayList<>();
}
I have trouble for these issues below and trying to implement equals and hashCode methods properly:
1) As far as I know, when there is a unique field that can be used to differentiate one person from the other e.g. email or username, then it is enough ONLY use these fields in equals and hashCode method. Is that true?
2) If there is not any unique field except from id, then should I add ALL the fields (id and the others) to equals and hashCode method implementation?
3) When using Hibernate and Data JPA, should I follow a different approach than other situation as there are 4 states transient, managed, removed, and detached in JPA lifecycle? I think id field should not be used in this situation as it is not present in transient mode? Right?
When implementing equals() and hashCode() methods:
If there is a unique field that can be used to differentiate one object from another, use only that field in the implementation.
If there is no unique field, use all the fields including the id in the implementation.
When using Hibernate and Data JPA, do not use the ID field in the implementation as it is not present in the transient state, instead use fields that are present in all states such as unique fields or all fields.
The problem with equals and hashCode is that their contract is broken for any mutable entity and with JPA, there aren't really any other.
Ignoring JPA for a moment, by definition the id of an entity defines its identity.
So it should be used for equals and hashCode.
But this requires the id to be unmodifiable in an entity, but JPA requires a no args constructor and a way to set all properties, including the id.
Probably the best way around this is to
use the id.
make sure that equals and hashCode is never ever used before the id is set, and the id is never changed afterwards.
Not changing the id after it is once set is normally not a problem, since the id shouldn't change from one value to another.
The problem is creation of new instances.
Again instances returned by JPA aren't a problem, because JPA will fully initialise them before returning them to you.
Creating fresh instances in your application is the problem.
Here you have the following options:
create the instance and immediately assign a id. UUIDs are perfect for this.
They can be generated easily and efficiently on the application server.
This could be done in a static factory method on the entity class.
The drawback is that UUIDs are a pain to work with for humans, since they are long and basically random.
They are also large and eat more memory in the database than a traditional sequence number.
But the use cases with so many rows that this actually is a problem are rare.
generate the id in the database as most people do, and make sure that your new entity gets saved immediately after creation.
This could be nicely done in a custom method in a repository.
But it does require that you set all required properties in one place, which often can be a problem.
Using some other attribute which is supposed to be immutable, like the account name or an email works only for very few entities in the first place and even for those the fact that it is immutable now doesn't mean it stays that way.
Instead of trying to avoid the pitfalls created by JPA you could alternatively rely on it.
JPA guarantees that for a given class and id only one instance is in a persistence context.
Therefore, as long as you only work within a single session/transaction with an entity and don't try to compare detachd entities, there is no need to implement equals and hashCode at all.
as you are already using lombok, you can use #Data annotation as well:
#Data
All together now: A shortcut for #ToString, #EqualsAndHashCode, #Getter on all fields, #Setter on all non-final fields, and #RequiredArgsConstructor!
I have data that flows through my application and normally it doesn't need to be bothered but to implement a new feature I need to store it temporarily (e.g. 1 hr). The data going in can be the exact same as something that is already in there so there is no need for a primary key. However, with JPA Entities need an Id but I don't need/want one. This is preventing me from getting it working.
This is through Spring using JPA. Since the data is moving in and out of the database frequently, the use of an auto generated ID is discouraged because it'll go through the IDs in a few years time. I have tried to make it embeddable to which it says I need to do a component scan to find where it is used but if I make it an entity then it gives me the error that it needs a primary key.
This is my entity that stores the data I need to persist.
#Entity
#Table(name = "quoteOrderHistory")
public class QuoteOrderHistory {
#Column(name = "storeNumber")
private int storeNumber;
#Column(name = "invoiceNumber")
private String invoiceNumber;
#Column(name = "quoteSaleDate")
private Date quoteSaleDate;
#Column(name="orderTotal")
private BigDecimal orderTotal;
#Column(name="orderHistoryDate")
private Timestamp orderHistoryDate;
// Constructors, Getters and Setters
}
This is my repository to access the data.
#Repository
public interface QuoteOrderHistoryRepository extends JpaRepository<QuoteOrderHistory, Long> {
#Query("DELETE FROM QuoteOrderHistory q WHERE q.orderHistoryDate > date")
void deleteAllExpired(Date date);
#Query("SELECT q FROM QuoteOrderHistory q WHERE q.storeNumber = ?1 AND q.invoiceNumber = ?2 ORDER BY q.orderHistoryDate DESC")
List<QuoteOrderHistory> findAllByStoreAndInvoiceDesc(int storeNumber, String invoiceNumber);
}
I can't figure out to get this to work. Again a primary key isn't needed since it's suppose to support duplicate entries. If there is another way around this without using JPA then I'm all for it but currently it seems to be the easiest to persist the data. If you need anymore information just let me know. I also might be missing something that can be done to avoid this all together but I'm not that familiar with JPA. So all help is appreciated.
You shouldn't run out of IDs for a column if you use the correct size. Stop trying to fight your framework and just add an auto-incrementing column.
https://hashrocket.com/blog/posts/running-out-of-ids
Let's say business is so good that we are inserting 10,000 records per
minute into our table. So, how long would it take to max out our
sequence? 1750380517 years
From How large can an id get in postgresql
Name Storage Size Description Range
smallint 2 bytes small-range integer -32768 to +32767
integer 4 bytes usual choice for integer -2147483648 to +2147483647
bigint 8 bytes large-range integer -9223372036854775808 to 9223372036854775807
serial 4 bytes autoincrementing integer 1 to 2147483647
bigserial 8 bytes large autoincrementing integer 1 to 9223372036854775807
If you're desperate to not use an id column for some reason I cannot possibly comprehend, it looks like you can do it in JPA by making every column part of the primary key description, but then your deletes and updates will delete/update any number of records. I HAVE NOT TRIED THIS. I WOULD NOT IMPLEMENT THIS ON A PRODUCTION SERVER.
https://en.wikibooks.org/wiki/Java_Persistence/Identity_and_Sequencing#No_Primary_Key
Sometimes your object or table has no primary key. The best solution
in this case is normally to add a generated id to the object and
table. If you do not have this option, sometimes there is a column or
set of columns in the table that make up a unique value. You can use
this unique set of columns as your id in JPA. The JPA Id does not
always have to match the database table primary key constraint, nor is
a primary key or a unique constraint required.
If your table truly has no unique columns, then use all of the columns
as the id. Typically when this occurs the data is read-only, so even
if the table allows duplicate rows with the same values, the objects
will be the same anyway, so it does not matter that JPA thinks they
are the same object. The issue with allowing updates and deletes is
that there is no way to uniquely identify the object's row, so all of
the matching rows will be updated or deleted.
If your object does not have an id, but its table does, this is fine.
Make the object an Embeddable object, embeddable objects do not have
ids. You will need a Entity that contains this Embeddable to persist
and query it.
Jazzepi stated was correct but I was strictly requested not to use an auto generated number as the ID. Therefore, people linked this here depicting using a UUID. This is the best choice for this problem since the objects in the database are timed to be in there no more than a few hours. Since this is the case, a UUID will never overflow and the likelihood of a repeated UUID inside of the table any given time is almost zero since most won't stay there.
New Entity class:
#Entity
#Table(name = "quoteOrderHistory")
public class QuoteOrderHistory {
#Id
#GeneratedValue(generator = "uuid")
#GenericGenerator(name = "uuid", strategy = "org.hibernate.id.UUIDGenerator")
#Column(name = "uuid", unique = true)
private String uuid;
#Column(name = "storeNumber")
private int storeNumber;
#Column(name = "invoiceNumber")
private String invoiceNumber;
#Column(name = "quoteSaleDate")
private Date quoteSaleDate;
#Column(name="orderTotal")
private BigDecimal orderTotal;
#Column(name="orderHistoryDate")
private Timestamp orderHistoryDate;
// Constructor, getters, setters
}
Do I need to add an index anoatation for the primary key of a hibernate table for decent performance, I assumed that marking a field with #id would mean an index was created
#Id
private String guid;
but I didnt notice anything being created in the ddl that was generated
But if I added an #index annotation
#Id
#org.hibernate.annotations.Index(name = "IDX_GUID")
private String guid;
then I do notice an index being created in the DDL.
So I'm thinking I need to do this for every table, but part of me is thinking is this really neccessary as surely hibernate would want indexes created for the primary key as a starting point ?
You do NOT have to create index explicitly. Instead of seeing DDL statements; I will recommend you to check the final schema created by hibernate. The index is created as part of create table statement.
How do I implement the following relationship using JPA?
table person (
id int,
name text
)
table person_home (
person_id int,
home_id int,
type char(1) -- 'p' = primary, 's' = secondary
)
table home (
id int,
address text
)
A person can have many homes, and a home can have many persons living in it (i.e. ManyToMany relationship).
Furthermore, a home can be a primary residence for one person, but a secondary residence for another person at the same time.
I'm not sure how to model this relationship, even though the database schema is clear.
I have thought of splitting mapping table person_home into person_primary_home and person_secondary_home, however I would prefer to retain the schema if possible.
This question is pretty much asked and answered here:
How to create a composite primary key which contains a #ManyToOne attribute as an #EmbeddedId in JPA?
You need four classes:
Person.java
Home.java
PersonHome.java
PersonHomePk.java
The Person.java and Home.java files you create with one to many relations to the PersonHome.java. They'll have #Id fields to identify the primary keys. Each will have a #OneToMany relation defined with at least a mappedBy attribute mapping to their respective fields in the PersonHome entity. i.e. in the Person.java you could have something like
#OneToMany(cascade = CascadeType.ALL, mappedBy = "Person")
private Collection<PersonHome> personHome;
The PersonHome.java will have an #EmbeddedId field to identify the PersonHomePk instance declaration which is its primary key (that is, instead of an #Id column you will have an #EmbeddedId annotating a declaration of a class representing the primary key of the join table PersonHome). Any other fields are declared as normal columns. The PersonHome.java will also declare two ManyToOne relations one each to person and home. These will use #JoinColumn annotation (make sure they have the attributes insertable=false and updatable=false). The datatypes will be the Person and Home classes. i.e.
#EmbeddedId
protected PersonHomePk personHomePk;
#Column (name = "type")
private String type;
#JoinColumn(name = "person_id", referencedColumnName = "person_id", insertable = false, updatable = false)
#ManyToOne(optional = false)
private Person person;
You'll need the same for the "Home" declaration too. Why are you using only a char for "type". I'd recommend a varchar so people who maintain the thing once you're gone will understand the code and database better when you aren't around. 'detached' is easier to understand the 'd'.
I believe if you're going to have metadata besides the relationship on the person_home table, you need to use three objects with two one-to-many relationships in order to be able to access all of the data.
You could eliminate this need by having two many-to-one relationships from the person table to the home table, by having primary_home_id and secondary_home_id -- unless I'm missing a requirement here and a person can have more than one primary or secondary home.
Create Table A (
ID varchar(8),
Primary Key(ID)
);
Create Table B (
ID varchar(8),
A_ID varchar(8),
Primary Key(ID),
Foreign Key(A_ID) References A(ID)
);
Given that I have created two tables using the SQL statements above, and I want to create Entity classes for them, for the class B, I have these member attributes:
#Id
#Column(name = "ID", nullable = false, length = 8)
private String id;
#JoinColumn(name = "A_ID", referencedColumnName = "ID", nullable = false)
#ManyToOne(optional = false)
private A AId;
In class A, do I need to reciprocate the many-to-one relationship?
#Id
#Column(name = "ID", nullable = false, length = 8)
private String id;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "AId")
private List<B> BList; //<-- Is this attribute necessary?
Is it a necessary or a good idea to have a reciprocal #OneToMany for the #ManyToOne? If I make the design decision to leave out the #OneToMany annotated attribute now, would that come back to bite me further along?
Is it a necessary or a good idea to have a reciprocal #OneToMany for the #ManyToOne?
No, it's not mandatory at all, it's a pure design decision. The whole question is... Do you want this (i.e. an uni-directional association):
Or this (i.e. a bi-directional association):
If you don't need to get Bs from A, then you can skip the bs attribute and the OneToMany on A side.
If I make the design decision to leave out the #OneToMany annotated attribute now, will come back to bite me further down.
No, and you can add it later if you discover that you need it.
They are optional. There is no need to add them to your model if you don't want to use them.
I'd sugguest to avoid the reverse mapping at all because such collections may become quite large and most persistance layers don't handle these very good. In many cases you'd have to deal with add/remove of already loaded/managed entities related to these collections yourself. So only add those if they really make things easier for you.
Sure not. Thats design decision between one vs two direction relationship. In most casses, a better choice is to have one direction relationship, especially if its a domain classes. doing this, your design will express better the mean of your domain.