checking uniq constraints without using primary key - java

I am using hibernate only with Annotations. My table looks something like this:
#Entity
#Table(name = "NetworkType",
uniqueConstraints = {#UniqueConstraint(columnNames = {"network_id", "type"})})
public class NetworkType implements Serializable {
#Id
private long id;
#Column(name = "network_id", nullable = false)
private long networkId;
#Column(name = "type", nullable = false)
private String type;
...
Currently when I write the same NetworkType twice, it throws an exception due to the UniqueConstraint (which is expected).
My thoughts are to just read the item first before checking. The problem is, my primary key is the Id, which I need because other tables references this table.
What's the best way to query for item for the "network_id" and "type" to verify the combination doesn't already exist?
I know I can do this with a Query manually, but is there a more Hibernate-y way of doing it?
In general, what's the proper way to "get" an object without using the PK? Are Criteria or Query the best way?

#UniqueConstraint is mainly used by database schema generation tools to create the data base schema. If used, they will generate the table with the columns mentioned in the #UniqueConstraint having unique constraint defined.
#UniqueConstraint doesn't have any impact/usage during data manipulation.
If you wish to achieve unique constraint behavior on network_id and type columns and your schema is already created, update your database schema to add the unique constraint on network_id and type columns. as below:
ALTER TABLE NetworkType
ADD CONSTRAINT uc_network_id_type UNIQUE (network_id, type)
Hope this helps!

Related

Hibernate + JPA does not find the column for an #ElementCollection table

I am trying to add an #ElementCollection but the column is not found after the setup, so I constantly receive an error. I use Spring + flyway for the set up. Everything happens in the public schema
So here is my big object:
#Entity
#Table(name = "my_big_table")
MyBigObject{
#Id
#Column(name=COL_ID)
#GeneratedValue(generator="gen_name")
#GenericGenerator(
name = "gen_name",
strategy = "seq_name"
)
#AttributeAccessor(CommonConstants.HIBERNATE_ACCESS_PROPERTY)
private long id;
...
...
#ElementCollection(fetch = FetchType.EAGER)
#CollectionTable(
name = "my_small_table",
joinColumns = #JoinColumn(name = "big_object_id")
)
private List<MySmallObject> mySmallObjects;
}
Here is my embedded object:
#Embeddable
public class MySmallObject {
#Column(name = "small_object_type")
private String smallObjectType;
}
Then besides the existing my_big_table table I add my_small_table using flyway
create table if not exists my_small_table
(
big_object_id bigint not null,
small_object_type varchar(64) not null
);
alter table my_small_table
add constraint FK_my_small_table
foreign key (big_object_id)
references my_big_table (id);
After this the my_small_table is successfully created but any instance of MyBigObject cannot be found because it looks for a column in the my_small_table that does not exist. As you can see it does not understand that the column name should use an underscore.
Big error trace ands with the following message:
Caused by: org.postgresql.util.PSQLException: ERROR: column mysmalltab0_.smallobjecttype does
not exist
09:17:24.994 INFO - STDOUT: Hint: Perhaps you meant to reference the column "mysmalltab0_.smallobjecttype".
Do you know what I could forget? Could lombock annotations that I also use for both classes spoil the picture?
As it's stated in the documentation:
By default, the placement of the #Id annotation gives the default access strategy. When placed on a field, Hibernate will assume field-based access. When placed on the identifier getter, Hibernate will use property-based access.
But the usage of the #AttributeAccessor leads to the changing access strategy for the field that hold #Id and as result your #Column(name = "small_object_type") annotation just was ignored. You can try to put it on the appropriate getter and it should work. But it's considered a good practiсe not to mix up access strategies for the entity fields.

Why we use #ForeignKey(name="FK_COUNTRY") annotation?

I have been going through some relational stuff in hibernate where I get this solution for relation between tables I tried this it works fine but when I remove #ForeignKey(name="FK_COUNTRY") nothing change then why are we using this annotation is that came under best practice?
#Entity
#Table(name = "state")
public class State {
#Id
#Column(name = "id")
private int id;
#Column(name = "name")
private String name;
#ManyToOne
#ForeignKey(name="FK_COUNTRY")
private Country country;
}
Hibernate should reflect DB structure,
You should read about the importance of foreign key
Referential Integrity
Easier Detective Work
Better performance
If you read the javadoc of #ForeignKey, you'll find:
Used to specify the handling of foreign key constraints when schema generation is in effect. If this annotation is not specified, the persistence provider's default foreign key strategy will be used.
If you don't generate the database schema from the class definitions (e.g. CREATE TABLE SQL statement), then the annotation has no effect.

JPA #UniqueConstraint doesn't see the columns

In Spring JPA I havean entity and I init the schema using FlywayDb.
My entity is:
#Entity
#Table(schema = "scheduler",
uniqueConstraints={#UniqueConstraint(name = "uq_task", columnNames = {"task", "date_at"})}
)
public class Task {
#Id
private Long id;
#Embedded
#Column(nullable = false)
private ITask task;
#Column(nullable = false)
private Date dateAt;
}
The schema is initialized as follows:
CREATE SCHEMA scheduler;
CREATE TABLE scheduler.task (
id bigserial primary key,
task bytea NOT NULL,
date_at timestamp NOT NULL
);
CREATE UNIQUE INDEX uq_task
ON scheduler.task(task, date_at);
Without the constraints on the entity, it works, with it doesn't. In particular I have the exception:
Caused by: org.hibernate.AnnotationException: Unable to create unique key constraint (task, date_at) on table task: database column 'task' not found. Make sure that you use the correct column name which depends on the naming strategy in use (it may not be the same as the property name in the entity, especially for relational types)
at org.hibernate.cfg.Configuration.buildUniqueKeyFromColumnNames(Configuration.java:1684)
at org.hibernate.cfg.Configuration.buildUniqueKeyFromColumnNames(Configuration.java:1616)
at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1452)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1846)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:857)
I use an H2 database.
ITask is an interface with several POJO implementations. ITask interface is annotated with #Embeddable.
My guess is that JPA tries to apply the unique constraints on columns that are not yet created by FlywayDb library. But this makes no sense to me.
Any idea?
After update of you question now I can guess that there is a problem with attribute in your ITask insterface please read that doc. In my opinion you have to override embbedable entity attribute to fix your problems.

How can I ask Hibernate to create an index on a foreign key (JoinColumn)?

Here is my model:
class User {
#CollectionOfElements
#JoinTable(name = "user_type", joinColumns = #JoinColumn(name = "user_id"))
#Column(name = "type", nullable = false)
private List<String> types = new ArrayList<String>();
}
As you can imagine there would be a table called "user_type", which has two columns, one is "user_id" and the other is "type".
When I use hbm2ddl to generate the ddls, I want to have this table, along with the foreign key constraint on "user_id".
However, there is no index for this column. How can I get hibernate to generate the index for me?
Try an #Index annotation.
#Index(name="user_type_index")
There is also an #IndexColumn annotation used with join tables, but it doesn't seem to actually create an index, but controls which field defines order in list semantics.
The #Index column in this context does seem to create an index on the join table.
I'm dealing with a similar issue and I've found that some dialects will automatically index foreign keys and others wont.
Hibernate Dialect class and all subclasses which do not override the getAddForeignKeyConstraintString method (Oracle, SQL Server, etc) will not create an index on the foreign key.
MySQLDialect overrides that method and adds an index to every foreign key

Does JPA support mapping to sql views?

I'm currently using Eclipselink, but I know now days most JPA implementations have been pretty standardized. Is there a native way to map a JPA entity to a view? I am not looking to insert/update, but the question is really how to handle the #Id annotation. Every entity in the JPA world must have an ID field, but many of the views I have created do not conform to this. Is there native support for this in the JPA or do I need to use hacks to get it to work? I've searched a lot and found very little information about doing this.
While using the #Id annotation with fields of directly supported types is not the only way to specify an entity's identity (see #IdClass with multiple #Id annotations or #EmbeddedId with #Embedded), the JPA specification requires a primary key for each entity.
That said, you don't need entities to use JPA with database views. As mapping to a view is no different from mapping to a table from an SQL perspective, you could still use native queries (createNativeQuery on EntityManager) to retrieve scalar values instead.
I've been looking into this myself, and I've found a hack that I'm not 100% certain works but that looks promising.
In my case, I have a FK column in the view that can effectively function as a PK -- any given instance of that foreign object can only occur once in the view. I defined two objects off of that one field: one is designated the ID and represents the raw value of the field, and the other is designated read-only and represents the object being referred to.
#Id
#Column(name = "foreignid", unique = true, nullable = false)
public Long getForeignId() {
...
#OneToOne
#JoinColumn(name = "foreignid", insertable=false, updatable=false)
public ForeignObject getForeignObject() {
...
Like I said, I'm not 100% sure on this one (and I'll just delete this answer if it turns out not to work), but it got my code past a particular crash point.
Dunno if it applies to your specific situation, though. And there's an excellent chance that after 11 months, you no longer care. :-) What the hell, that "Necromancer" badge doesn't just earn itself....
In my view I have a "unique" id, so I mapped it as the Entity id.
It works very well:
#Entity
#Table(name="table")
#NamedQuery(name="Table.findAll", query="SELECT n FROM Table n")
public class Table implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name="column_a")
private int columnA;
JPA - 2.5.4
CREATE MATERIALIZED VIEW IF NOT EXISTS needed_article as select product_id, count(product_id) as count from product_article group by product_id;
CREATE MATERIALIZED VIEW IF NOT EXISTS available_article as select product_id, count(product_id) as count from article a inner join product_article p
on a.id = p.article_id and a.stock >= p.amount_of group by product_id;
CREATE UNIQUE INDEX productId_available_article ON available_article (product_Id);
CREATE UNIQUE INDEX productId_needed_article ON needed_article (product_Id);
Entity.java
#Entity
#Immutable // hibernate import
#Getter
#Setter
public class NeededArticle {
#Id
Integer productId;
Integer count;
}
Repository.java
#Repository
public interface AvailableProductRepository extends CrudRepository<AvailableArticle, Integer> {
#Query("select available.productId from AvailableArticle available, NeededArticle needed where available.productId = needed.productId and available.count = needed.count")
List<Integer> availableProduct();

Categories