Hibernate Search 6: Methods mapping - java

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.

Related

Hibernate SchemaFilterProvider get Java entity name

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

Spring JPA + Hibernate Search : How update the Search Index(Lucene) only?

I use Spring JPA + Hibernate Search to implement persistant and search in my application.
I have models like this
public class FeatureMeta {
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
#Column(unique=true)
private String uri;
#Column
#Field
private String name;
#Field
#Column
private String businessDesc;
#Field
#Column
private String logicalDesc;
.
.
#Field
#Column(insertable=false,updatable=false)
private Long totalDownloads;
.
.
}
To give the idea about this class, "FeatureMeta" maintains meta-data information which updates very rarely.
However the field "totalDownloads" is constantly changing whenever user download information about this "feature".
Basically "totalDownloads" is not part of the meta-data but I had to put this field in the model because I need to show the "totalDownloads" in the search result of "feature search".
I use same JPA Repository which updates both MySQL and Lucene index.
My question is ; Is it possible to only update the "totalDownloads" in the Lucene Index but not the entity in MySQL whenever change is done to the "totalDownloads" field ?
You'll have to use the #Transient annotation to mark that you don't want this attribute part of your database model.
#Field
#Transient
private Long totalDownloads;
Making the field transient also means it won't be loaded from the database (completely ignored by Hibernate ORM, but not by Hibernate Search); if that's not what you intended you could add an additional field: map one to Hibernate ORM and the other indexed with Hibernate Search and annotated with #Transient. In this case you'll have to make the setter update both fields.
You will likely need to change this configuration property too:
hibernate.search.enable_dirty_check = false
as Hibernate Search will otherwise not generate any change in the Lucene index, in case the entity has no other changes.

How to customize the spring-data Repository method names?

I've a bunch of entities that use an underscore prefix before the field names and otherwise use camelcase.
#Entity
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long _id;
private String _firstName;
private String _lastName;
#OneToOne
private Foo _foo;
// … methods omitted
}
The repository
public interface CustomerRepository extends PagingAndSortingRepository<Customer, Long> {
Iterable<Customer> findByFoo(Foo foo);
}
The corresponding fields in the tables also use this naming scheme:
Customer: _id, _firstName, _lastName, _foo__id
Now that I am migrating this project to spring-data, I'm getting many IllegalArgumentExceptions with :
Could not create query metamodel for method
public abstract java.lang.Iterable
com.example.repository.CustomerRepository.findByFoo(com.example.model.Foo)!
Unable to locate Attribute with the given name [foo] on this ManagedType [com.example.model.Customer]
I have no need to change hibernate's naming strategy, but how can I change the query method generation naming algorithm to map "findByFoo" -> "_foo" on the entity, or in JPQL terms "where x._foo__id = ?1"
I'm using old school xml configuration, and no spring boot.
Edit: found this in the docs, which isn't helpful..
"As we treat underscore as a reserved character we strongly advise to
follow standard Java naming conventions (i.e. not using underscores in
property names but camel case instead)."
Perhaps I should refactor the field names, drop the underscore, and then implement a hibernate naming strategy that adds the underscore back in?
I can just repeat what's in the docs (although I'd be interested in the "isn't helpful" part). Use standard Java conventions for your Java code. Use the store specific means to customize the way properties are mapped onto database tables. JPA provides #Column annotations for that.

JPA - how to reference attributes in queries if AccessType is "property"?

I would like to have an entity whos attributes are called by JPA per getter methods instead of direct field access. So I use #Access(AccessType.PROPERTY). Everything's fine so far. But when I specify a NamedQuery with referencing the attributes, the following compiler error is shown:
Multiple markers at this line
- The state field path 'u.password' cannot be resolved to a valid type.
- The state field path 'u.login' cannot be resolved to a valid type.#
The code is:
#Entity
#Access(AccessType.PROPERTY)
#NamedQuery(name="User.validateLogin", query="SELECT u FROM User u WHERE u.login = :login AND u.password = :password")
public class User implements Serializable {
Is there a way to set the AccessType to PROPERTY AND using queries with references to attributes?
I found the answer. If you use the default (AccessType.FIELD), than a few things happen automatically (you can use the attributes within queries for instance).
If you use AccessType.PROPERTY then you've to set the Annoation #Basic in front of the getter methods.

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