Repetition of constraint in liquidbase and entity definition - java

I`ve noticed that in my project some composite constraint are set in a liquidbase,
for example
constraint importer_ukey unique (name, country_id, is_importer, is_manufacturer)
but then they are repeated in entity definition in #Table annotation
#Table(uniqueConstraints = {#UniqueConstraint(name = "importer_ukey",
columnNames = {"name", "country_id", "is_importer", "is_manufacturer"})})
Is there any positive in repetition of uniqueConstraint in entity ?

Other than for creating the schema based on the JPA annotations, it is also a needed at runtime for the JPA provider to order INSERT, UPDATE and DELETE statements correctly.
Unique constraints ensure that the data in a column or combination of columns is unique for each row. A table's primary key, for example, functions as an implicit unique constraint. In JPA, you represent other unique constraints with an array of UniqueConstraint annotations within the table annotation. The unique constraints you define are used during table creation to generate the proper database constraints, and may also be used at runtime to order INSERT, UPDATE , and DELETE statements. For example, suppose there is a unique constraint on the columns of field F. In the same transaction, you remove an object A and persist a new object B, both with the same F value. The JPA runtime must ensure that the SQL deleting A is sent to the database before the SQL inserting B to avoid a unique constraint violation.
Source: https://openjpa.apache.org/builds/1.0.2/apache-openjpa-1.0.2/docs/manual/jpa_overview_mapping_unq.html
So yes: it is important.

Related

Why does 'delete()' method in CrudRepository require entity with unique id?

In the description of the delete method in org.springframework.data.repository,CrudRepository interface it is written only that it deletes a given entity and that it accepts entity object itself.
It specifies nothing about entity's id needing to be unique.
However, this method works only when entity has an unique id. If the table may have multiple rows with the same id, this method fails when entity whose id is not unique is attempted to be deleted (at least in my case).
Now I understand that it is a very bad thing that an id is not unique in the table, but, in theory, it should work since this method accepts the entire entity as a parameter, and every entity (when all its columns are combined) in my table is unique. There are no two identical rows in a table since table has an unique constraint on a combination of all its columns.
Sure, there are other methods, like deleteById which would fail since they only accept id as a parameter and not the whole entity and since id is not unique, Spring does not know which entity to delete. Sure.
But why delete method fails when it should be able to distinguish between entities with the same id since it accepts the whole entitiy as a parameter (thus giving it access to all other columns of the entity and not just the id column)?
The requirement for the JPA id is that it is a primary key i.e. uniquely identifies the row. If you do not follow this, bad things will happen. You can have a composite primary key though, as it seems your row is identified by multiple columns. Look into #Embeddable/#EmbeddedId mappings for this purpose.

How to map Map<String, List<String>> in hibernate

I am using currently Spring Data JPA and I would like to map an attribute
#Entity
public class Outer {
...
Map<String, List<String>> typesToCategories;
}
Let's assume I have a tables outer and outer_type_category. The first one is trivial: only column outer_id is relevant from it
CREATE TABLE outer_types_categories (
id uuid NOT NULL,
outer_id uuid NOT NULL,
type character varying(128) NOT NULL,
category character varying(128) NOT NULL,
...
)
Which annotations should I use (if it is possible in general) to map this table to the map?
I have tried to use this
#ElementCollection
#CollectionTable(name = "outer_type_category", joinColumns = [JoinColumn(name = "outer_id")])
#MapKeyColumn(name = "type")
#Column(name = "category")
Map<String, List<String>> typesToCategories;
but in the end I see an exception:
Caused by: org.hibernate.MappingException: Could not determine type for: java.util.List, at table: outer_type_category, for columns: [org.hibernate.mapping.Column(category)]
Did I forget anything?
JPA specifies that
A persistent field or property of an entity or embeddable class may
correspond to a collection of a basic type or embeddable class
(“element collection”).
(JPA 2.2 Specification, section 2.6; emphasis added)
java.util.List is neither a basic type nor an embeddable class, so it is not among the allowed element types of an element collection. Moreover, JPA goes on to say
An embeddable class [...] that is contained within an element
collection must not contain an element collection
, so even replacing List with an embeddable class does not give you a suitable mechanism for mapping the structure you've described. If you're unwilling or unable to change the DB structure at all, then I don't think you can map your table in a manner that is in any way analogous to what you describe.
If you can at least add a new table to the DB then you can introduce a new entity representing an entry in your map, say OuterType, which has an element collection mapped to your outer_types_categories table. It would probably need to have a composite ID corresponding to (outer_id, type). Even then, the DB side would need to be set up to automatically assign values to the id column of the collection table (unless you can drop that column, which in truth does not appear to be useful for your apparent purposes), because members of an element collection are not entities, and therefore JPA does not ascribe IDs to them. Moreover, it is messy (on the JPA side) to have a column that is both part of a composite primary key and a foreign key for a related entity, as this would require.
If you have more freedom to modify the DB structure then I'd set up the aforementioned OuterType entity with a standard, surrogate ID and a bidirectional one-to-one relationship with Outer, represented on the Outer side as a map. Set up an element collection containing the category strings in OuterType using the default mapping strategy, which would use OuterType's (surrogate) ID and neither its "type" nor its "outer_id" in the collection table.

No identifier specified for entity in java hibernate

I am using hibernate and i mapped my table with my bean. If i am not assign any particular column value as #id, it throws "No identifier specified for entity" error, however it is not primary key in my data table. I want to add multiple records with same data. how can i do it? When i annotated my product Name column with #id my code runs perfect.
Hibernate requires an identifier for each entity. However, it is possible to use native queries to insert new records. And, in the same way, to recover them.

CRUD operation using Hibernate on table without Primary key [duplicate]

I making a sample application with Hibernate. My requirement is that there is no primary key on the table. I had to do only select query from application. I know there should be a primary key, but the table I am referring has been made without it.
It has about 50k records. So, modifying the table to add ID column does not see viable option.
Can it possible?
Hibernate requires that entity tables have primary keys. End of story.
50k records is simply not that many when you're talking about a database.
My advice: add an autoincrement integer PK column to the table. You'll be surprised at how fast it is.
So, modifying the table to add ID column does not see viable option.
Why's that? Do you just mean, because it already has fifty thousand records? Trust me, that's really not very many.
Even if the table doesn't have a surrogate key, and doesn't have a primary-key constraint, and even if you're not willing to alter the table, it still probably has some sort of candidate key — that is, some set of columns that are never null, and whose values uniquely identify a record. Even without altering the table to enforce their uniqueness and non-nullity, you can tell Hibernate that those columns form a composite ID.
I have found solution for tables without primary key and null as values. It will work on oracle DB. Maybe something similar exists for other DBs.
You should create new primary key in the POJO class:
#Id
#Column(name="id")
private Integer id;
and use createNativeQuery like this
getEntityManager().createNativeQuery("select rownum as id, .....
The native query will generate primary key and you will get unique results.
Not with Hibernate. It requires a primary key.
Using DB2 with a table without primary keys the following works:
Define the primary key field in the Entity similar to:
#Id
#Column(name="id")
private Integer id;
For Select Query add:
String selectQuery = "select ROW_NUMBER() OVER () id, ..."
I haven't tested with updates to the entity but selecting the info from the DB works fine

JPA entity for a table without primary key

I have a MySQL table without primary key, and I have to map it into a JPA entity. I cannot modify the table in any way.
Because entities must have a primary key, I have to specify one. If I'm certain that the field I use as a primary key in the entity (or the fields, should I opt for using composite primary key) will always be unique (and not null) in table, can the fact that the table doesn't have a primary key specified in CREATE TABLE cause any issues?
That's correct. JPA has no way of knowing if the column(s) it is using as a PK is actually a real PK in the database. If those column(s) are, in practice, a PK, then it should be fine.
You may potentially get some performance problems if the pseudo-PK columns are not correctly indexed, though - JPA will execute queries against the PK on the assumption that it will perform well.
JPA itself doesn't analyze your database. Just don't use common methods using primary key (find/merge/...) instead use named queries, for example using jpql update syntax.
#Entity
#Table(name = "login")
#NamedQueries({
#NamedQuery(name = "Login.updateLastOnline",
query = "UPDATE Login l SET l.lastOnline = :newDate WHERE l.loginId = :loginId")
})
public class Login implements Serializable
{
It doesn't matter if loginId is primary key

Categories