Hibernate SchemaFilterProvider get Java entity name - java

I would like Hibernate to disable certain classes from being validated on startup.
My particular use-case:
spring.jpa.hibernate.ddl-auto=validate
#Table (name = "SAME_TABLE")
public class Entity1 {
#Column
private Long value;
// rest of values
}
#Table (name = "SAME_TABLE")
public class SearchEntity2 {
#Column
private String value;
// rest of values
}
As you can see I have two classes mapped to the same table called SAME_TABLE. This is because I want to do wildcard searches on numeric field value
JPA Validation fails on Oracle (h2 succeeds suprisingly) because it detects that the String is not NUMERIC(10).
This question here by #b0gusb provides an excellent way of filtering out via table name:
How to disable schema validation in Hibernate for certain entities?
Unfortunately my table name is identical. Is there any way of getting to the Java class name from SchemaFilteror perhaps another way of doing this?
Thanks
X

Related

spring / hibernate: Generate UUID automatically for Id column

I am trying to persist a simple class using Spring, with hibernate/JPA and a PostgreSQL database.
The ID column of the table is a UUID, which I want to generate in code, not in the database.
This should be straightforward since hibernate and postgres have good support for UUIDs.
Each time I create a new instance and write it with save(), I get the following error:
o.h.j.JdbcSQLIntegrityConstraintViolationException: NULL not allowed for column "ID"; SQL statement: INSERT INTO DOODAHS (fieldA, fieldB) VALUES $1, $2) ...
This error indicates that it's expecting the ID column to be auto-populated (with some default value) when a row is inserted.
The class looks like this:
#lombok.Data
#lombok.AllArgsConstructor
#org.springframework.data.relational.core.mapping.Table("doodahs")
public class Doodah {
#org.springframework.data.annotation.Id
#javax.persistence.GeneratedValue(generator = "UUID")
#org.hibernate.annotations.GenericGenerator(name="UUID", strategy = "uuid2")
#javax.persistence.Column(nullable = false, unique = true)
private UUID id;
//... other fields
Things I have tried:
Annotate the field with #javax.persistence.Id (in addition to existing spring Id)
Annotate the field with #org.hibernate.annotations.Type(type = "pg-uuid")
Create the UUID myself - results in Spring complaining that it can't find the row with that id.
Specify strategy = "org.hibernate.id.UUIDGenerator"
Annotate class with #Entity
Replace spring #Id annotation with #javax.persistence.Id
I've seen useful answers here, here and here but none have worked so far.
NB the persistence is being handled by a class which looks like this:
#org.springframework.stereotype.Repository
public interface DoodahRepository extends CrudRepository<Doodah, UUID> ;
The DDL for the table is like this:
CREATE TABLE DOODAHS(id UUID not null, fieldA VARCHAR(10), fieldB VARCHAR(10));
Update
Thanks to Sve Kamenska, with whose help I finally got it working eventually. I ditched the JPA approach - and note that we are using R2DBC, not JDBC, so the answer didn't work straight away. Several sources (here, here, here, here, here and here) indicate that there is no auto Id generation for R2DBC. So you have to add a callback Bean to set your Id manually.
I updated the class as follows:
#Table("doodahs")
public class Doodah {
#org.springframework.data.annotation.Id
private UUID id;
I also added a Bean as follows:
#Bean
BeforeConvertCallback<Doodah> beforeConvertCallback() {
return (d, row, table) -> {
if (d.getId() == null){
d.id = UUID.randomUUID();
}
return Mono.just(d);
};
}
When a new object (with id = null, and isNew = true) is passed to the save() method, the callback method is invoked, and it sets the id.
Initially I tried using BeforeSaveCallback but it was being called too late in the process, resulting in the following exception:
JdbcSQLIntegrityConstraintViolationException: NULL not allowed for column "ID"....
Update
There are, at least, 2 types of Spring Data: JPA and JDBC.
The issue happens because you are mixing the 2 of them.
So, in order to fix, there are 2 solutions.
Solution 1 - Use Spring Data JDBC only.
Pom.xml dependency
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
Generate ID.
Spring Data JDBC assumes that ID is generated on database level (like we already figured that out from log). If you try to save an entity with pre-defined id, Spring will assume that it is existing entity and will try to find it in the database and update. That is why you got this error in your attempt #3.
In order to generate UUID, you can:
Leave it to DB (it looks like Postgre allows to do it)
or Fill it in BeforeSaveCallback (more details here https://spring.io/blog/2021/09/09/spring-data-jdbc-how-to-use-custom-id-generation)
#Bean BeforeSaveCallback<Doodah> beforeSaveCallback() {
return (doodah, mutableAggregateChange) -> {
if (doodah.id == null) {
doodah.id = UUID.randomUUID();
}
return doodah;
};
}
Solution 2 - Use Spring Data JPA only
Pom.xml dependency
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
Generate ID.
Here you can, actually, use the approach with the UUID auto-generation, like you wanted to do initially
Use javax.persistence #Entity annotation instead of springdata #Table on the class-level
and Use #javax.persistence.Id and #javax.persistence.GeneratedValue with all defaults on id-field.
#javax.persistence.Id
#javax.persistence.GeneratedValue
private UUID id;
Other notes:
Specification of generator and strategy is not required, since it will generate based on the type of the id field (UUID in this case).
Specification of Column(nullable = false, unique = true) is not required either, since putting #Id annotation already assumes these constraints.
Initial answer before update
The main question: how do you save the entity? As id-generation is handled by JPA provider, Hibernate in this case. It is done during save method of em or repository. In order to create entities and ids Hibernate is looking for javax.persistence annotations, while you have Spring-specific, so I am wandering how do you save them.
And another question here: the error you provided INSERT INTO DOODAHS (fieldA, fieldB) VALUES $1, $2 shows that there is no id field in the insert-query at all. Did you just simplified the error-message and removed ID from it? Or this is original error and your code does not even "see" field ID? In that case the issue in not related to the id-generation, but rather is related to the question why your code does not see this field.

Hibernate Search 6: Methods mapping

In Hibernate Search 5.x I can map entity method as the fulltext field like this:
#Entity
public class Person {
#Id
#GeneratedValue
private Long id;
private String name;
private String surname;
public String getWholeName() {
return name + " " + surname;
}
// getters, setters
}
// Mapping configuration, programmatic approach
SearchMapping sm = new SearchMapping();
sm
.entity(Person.class)
.indexed()
.property("wholeName", ElementType.METHOD)
.field();
Then I have a field with name "wholeName" in my fulltext index and it contains return value of getWholeName() method.
How to do it in Hibernate Search 6? I found only a way how to map an entity field but not a method. Thank you!
Short answer
If there is no field named wholeName, Hibernate Search 6 will automatically fall back to the getter. The ElementType from Hibernate Search 5 is no longer necessary, and that's why it was removed.
Note that Hibernate Search is also smarter when it comes to detecting changes in entities. That's usually great, but the downside is that you'll need to tell Hibernate Search what other attributes wholeName relies on. See this section of the documentation (you can also find an example using the programmatic mapping API here).
Long answer
When an attribute has a field but no getter, or a getter but no field, there is no ambiguity. Hibernate Search uses the only available access type.
When an attribute has both a field and a getter, there is a choice to be made. Hibernate Search 6 chooses to comply with Hibernate ORM's access type.
Hibernate ORM accesses attributes either by direct access to the field ("field" access type) or through getters/setters ("property" access type).
By default, the access type in Hibernate ORM is determined by where your #Id annotation is. In this case, the #Id annotation is located on a field, not a method, so Hibernate ORM will use the "field" access type. And so will Hibernate Search.
You can also set the Hibernate ORM access type explicitly using the #Access annotation, either for the whole entity (put the annotation on the class) or for a particular property (put the annotation on the field). Hibernate Search will comply with this too.

Can I create a entity mapping to table which has a name from specific property of the entity?

For example, I have an entity below.
#Entity
public class Indexer
#NotNull #Id
private long id;
#Column
private string volumeKey;
}
I want to create a table with a ‘volumeKey’ property in this entity.
For example, A indexer has a ‘X12372’ as a volumeKey of property. I want this entity to be mapped to ‘INDEXER_X12372’.
And I tried to create custom NamingStrategy class for Indexer. And I can’t get an entity to be mapped in this class for making a table of name from.
You want the table to be used to be determined by a value of a property.
This is not possible with JPA or Spring Data JPA.
But some (many?) databases can do this transparently with partitioned tables.
See https://docs.oracle.com/cd/B28359_01/server.111/b32024/partition.htm for Oracle documentation as an example.
It should be easy enough to find a similar document for the database you use.

How to generate domain from an enum while generating DDL from data model?

Is there a way to generate a domain for a field that is defined as enum while generating DDL from the data model?
The default behaviour for fields that are defined as enum is either EnumType.STRING or EnumType.ORDINAL. In this particular case I use #Enumerated(EnumType.STRING).
MyEnum.java
public enum MyEnum {
MY_VALUE_1, MY_VALUE_2, MY_VALUE_3
}
MyEntity.java
#javax.persistence.Entity
public class MyEntity {
#javax.persistence.Id
private long id;
#javax.persistence.Enumerated(javax.persistence.EnumType.STRING)
private MyEnum status;
}
This is unfortunately defined as "only" character varying(255) (I am using PostgreSQL RDBMS).
ALTER TABLE myentity ADD COLUMN status character varying(255);
This of course gives an opportunity to put garbage there that has nothing to do with the actual enum.
INSERT INTO myentity(id, status) VALUES (1, 'THIS_HAS_NOTHING_TO_DO_WITH_THE_ENUM');
In this place I would like to be able to give a hint to Hibernate to generate a domain with only those values allowed that actually belong to the enumeration.
As a workaround I wanted to generate a check constraint using #Check annotation. This however regrettably cannot be done dynamically. I wanted to loop through the MyEnum.values() in order to generate this check so that I do not have to change it at the same time when the enum gets additional values.
public class MyEntity {
#javax.persistence.Id
private long id;
#javax.persistence.Enumerated(javax.persistence.EnumType.STRING)
#org.hibernate.annotations.Check(constraints = String.format("currentclub in %s", PlayerStatus.values()))
private MyEnum status;
}
This of course leads to a syntax error
The value for annotation attribute Check.constraints must be a constant expression.
which is clear to me as the attribute value must be determined at compilation time.
Is there any other way to do it in a clever way?

Is the JPA #Embedded annotation mandatory?

I have tried omitting the #Embedded annotation and still the fields have been embedded in the table. I cannot find anything which would say that the #Embedded annotation is optional.
Is it or is it not optional?
The following code
#Embeddable
public class Address {
String city;
String street;
}
#Entity
public class Person {
String name;
#Embedded // it seems that it works even if this annotation is missing!?
Address address;
}
generates always the same table
person
name
city
street
even if I do not specify #Embedded.
My configuration:
JBoss EAP 6.4.0
hibernate-jpa-2.0-api-1.0.1.Final-redhat-3.jar
The JPA specification says:
http://docs.oracle.com/javaee/7/api/javax/persistence/Embedded.html
#javax.persistence.Embedded
Specifies a persistent field or property of an entity whose value is an instance of an embeddable class. The embeddable class must be annotated as Embeddable.
http://docs.oracle.com/javaee/7/api/javax/persistence/Embeddable.html
#javax.persistence.Embeddable
Specifies a class whose instances are stored as an intrinsic part of an owning entity and share the identity of the entity. Each of the persistent properties or fields of the embedded object is mapped to the database table for the entity.
In case of using Hibernate it does not matter if you annotate the field itself (as #Embedded) or if you annotate the referenced class (as #Embeddable). At least one of both is needed to let Hibernate determine the type.
And there is a (implicit) statement about this inside the Hibernate documentation, take a look here:
http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/mapping.html#mapping-declaration-component
It says:
The Person entity has two component properties, homeAddress and
bornIn. homeAddress property has not been annotated, but Hibernate
will guess that it is a persistent component by looking for the
#Embeddable annotation in the Address class.
Embedded-Embeddable is not mandatory, but it gives you nice OOP perspective of your entities' relationship. Another way to do such a thing - is to use OneToOne mapping. But in such a case entity WILL be written to separate table (while in case of embedded it CAN be written to the separate table in your DB).

Categories