How to customize the spring-data Repository method names? - java

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.

Related

Using UUID vs #Id annotation in spring project

I'm very new to Spring/Springboot and have seen different approaches in tutorials regarding the model classes used to represent database objects. I was just wondering when it's appropriate to use which?
Approach 1:
A basic class to model a user object
public class User {
private final UUID id;
// other fields
public User(UUID id, <other fields>) {
this.id = id;
// set other fields
}
In the repository layer, we might have a DAO which looks something like
#Repository
public interface UserDao {
public int createUser(UUID id, <other fields>);
// other CRUD operations
}
When the user doesn't input a valid UUID (or absent) a default method could insert it by calling UUID.randomUUID()
Approach 2:
Instead of using a UUID as a unique identifier, instead, with something like Hibernate/JPA we use the #Entity annotation on the User class in the model package, and have the PK field annotated with #Id
#Entity
public class User {
#Id
private final long id;
// other fields
}
#Id annotation is the most commonly used approach in Hibernate. This will map a Java String / BigDecimal / long attribute to an identifier. And using this, you can use specify four generation strategies - AUTO, IDENTITY, SEQUENCE and TABLE.
UUIDs are used when you want your primary key to be globally unique. I can think of a few scenarios where you might want this -
You have data in multiple databases and your keys needs to be unique across different databases.
You need your generated id value even before you persist your record in your database for specific business purposes.
But the downside is that, UUIDs are long and may cost more in terms of storage space.

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.

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).

Specify MongoDb collection name at runtime in Spring boot

I am trying to reuse my existing EmployeeRepository code (see below) in two different microservices to store data in two different collections (in the same database).
#Document(collection = "employee")
public interface EmployeeRepository extends MongoRepository<Employee, String>
Is it possible to modify #Document(collection = "employee") to accept runtime parameters? For e.g. something like #Document(collection = ${COLLECTION_NAME}).
Would you recommend this approach or should I create a new Repository?
This is a really old thread, but I will add some better information here in case someone else finds this discussion, because things are a bit more flexible than what the accepted answer claims.
You can use an expression for the collection name because spel is an acceptable way to resolve the collection name. For example, if you have a property in your application.properties file like this:
mongo.collection.name = my_docs
And if you create a spring bean for this property in your configuration class like this:
#Bean("myDocumentCollection")
public String mongoCollectionName(#Value("${mongo.collection.name}") final String collectionName) {
return collectionName
}
Then you can use that as the collection name for a persistence document model like this:
#Document(collection = "#{#myDocumentCollection}")
public class SomeModel {
#Id
private String id;
// other members and accessors/mutators
// omitted for brevity
}
It shouldn't be possible, the documentation states that the collection field should be collection name, therefore not an expression:
http://docs.spring.io/spring-data/data-mongodb/docs/current/api/org/springframework/data/mongodb/core/mapping/Document.html
As far as your other question is concerned - even if passing an expression was possible, I would recommend creating a new repository class - code duplication would not be bad and also your microservices may need to perform different queries and the single repository class approach would force you to keep query methods for all microservices within the same interface, which isn't very clean.
Take a look at this video, they list some very interesting approaches: http://www.infoq.com/presentations/Micro-Services
I used #environment.getProperty() to read from my application.yml. Like so :
application.yml:
mongodb:
collections:
dwr-suffix: dwr
Model:
#Document("Log-#{#environment.getProperty('mongodb.collections.dwr-suffix')}")
public class Log {
#Id
String logId;
...

Xtend's _field naming and SpringData's repository naming conventions

It seems that the naming conventions between Xtend and Spring Data are incompatible.
For example:
// User.xtend
class User {
#Property
var Long id;
}
interface UserRepository extends JpaRepository<User> {
public User findById(Long id)
}
The #Property annotation renames id to _id, which causes Spring Data to fail, claiming No property id found
Is there a way to either:
Suppress Xtend's renaming of the field
"Teach" Spring Data about the naming convention (Looking for a field? Add an underscore)
Instruct Spring Data to use property-access, rather than field-access for the property resolution?
Any of these would solve this issue, I believe.
Since 2.7.3 the #Property annotation has been superceded by #Accessors, which no longer prepends the fields with an underscore.
Before 2.7.3 you have to build your own #Property annotation which doesn't prepend an underscore to the field's name.
See http://www.eclipse.org/xtend/documentation.html#activeAnnotations
(updated)

Categories