I have several POJOs which will have a monetary amount. My idea is to create a generic object MonetaryAmount (consisting of a currency and a value), which will then be used whenever I want to represent a monetary amount in one of my POJOs:
public class MonetaryAmount {
private String currency;
private BigDecimal value;
}
public class Account {
#Column(name = "ACCOUNT_NAME")
private String name;
private MonetaryAmount balance; // TODO set column annotation values of currency and value
}
Since MonetaryAmount will be used in several POJOs, I couldn't annotate the currency and value attributes with the #Column since the column name will not always be the same in all cases. Is there any way to annotate MonetaryAmount attributes (e.g. balance in the example above) to provide the column name for the currency and value attributes in a way that jOOQ understands them when mapping/unmapping a POJO similar to how Hibernate interprets the #AttributeOverride annotation please?
The #Embeddable annotation is currently (jOOQ 3.11) not supported by jOOQ's DefaultRecordMapper yet. The relevant feature requests are:
https://github.com/jOOQ/jOOQ/issues/2360
https://github.com/jOOQ/jOOQ/issues/2530
https://github.com/jOOQ/jOOQ/issues/6518
What you can do already now, if you're not using the JPA annotations on your POJOs, is to use the following aliasing notation in your query:
ctx.select(
ACCOUNT.ACCOUNT_NAME.as("name"),
ACCOUNT.CURRENCY.as("balance.currency"),
ACCOUNT.VALUE.as("balance.value"))
.from(ACCOUNT)
.fetchInto(Account.class);
This feature is documented in DefaultRecordMapper, see:
If Field.getName() is MY_field.MY_nested_field (case-sensitive!), then this field's value will be considered a nested value MY_nested_field, which is set on a nested POJO that is passed to all of these (regardless of visibility):
Single-argument instance method MY_field(...)
Single-argument instance method myField(...)
Single-argument instance method setMY_field(...)
Single-argument instance method setMyField(...)
Non-final instance member field MY_field
Non-final instance member field myField
Assuming Hibernate : You can used Embedded components.
#Entity
public class Account implements Serializable{
#Column(name = "ACCOUNT_NAME")
private String name;
#Embedded
#AttributeOverrides( {
#AttributeOverride(name="currency", column = #Column(name="CURRENCY") ),
#AttributeOverride(name="value", column = #Column(name="VALUE") )
} ) private MonetaryAmount balance;
}
#Embeddable
public class MonetaryAmount implements Serializable{
private String currency;
private BigDecimal value;
}
Though this should work, I think in your case you should try inheritance and still use same approach to override attributes in Object Oriented way.
Related
I am trying to use the Class-based Projections to fill the data but seems the Spring JPA does not support the nested projection. Here is my entity class:
public class Category extends BaseEntity<String> {
#Column(unique = true)
private String code;
private String externalCode;
#ManyToOne(cascade = CascadeType.ALL)
private Category parent;
..
}
Here is the DTO class for same:
#Data
#NoArgsConstructor
#AllArgsConstructor
#Builder
public class CategoryDto implements BaseDto, Serializable {
private String code;
private String externalCode;
private CategoryDto parent;
..
}
My CategoryRepository
#Query("select new com.easycart.core.data.category.CategoryDto(c.id,c.code,c.externalCode,c.seoMeta, c.createdAt, c.updatedAt,c.parent) FROM Category c where c.code = :code")
CategoryDto findCategoryByCode(String code);
I can't use the c.parent as the type is Category and not the CategoryDto, also I did not find any option to use the nested projection to fill the parent information for the given entity. Can someone help me with following questions?
Is there a way to achieve this using class based projection?
DO I need to fallback to the option to fill the parent information separately (I don't need lot of information for the parent in the initial load.).
Any other way to achieve this? I don't want to use the interface based projection as initial test showing it's very slow as compare to class based projection.
There is no out of the box support for this in Spring Data JPA.
The way to achieve this is to use constructor expressions and ResultTransformer
I'm trying to implement inheritance with Kotlin and JPA. My abstract base class (annotated with #Entity) holds the ID (annotated with #Id and #GeneratedValue) and other metadata, like createDate, etc. I'm getting several errors from Hibernate, one for each field except the ID:
org.hibernate.tuple.entity.PojoEntityTuplizer - HHH000112: Getters of lazy classes cannot be final: com.example.BaseEntity.createDate
As I've read I need to include the open keyword for each property.
I have 3 questions regarding this:
Why do I have to do that in the superclass, and don't need in subclass? I'm not overriding those properties.
Why isn't it complaining about the ID?
It seems to work without the open keyword, then why is the error logged?
Edit:
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
abstract class BaseEntity(
#Id #GeneratedValue(strategy = GenerationType.IDENTITY) val id: Long = 0,
val createdAt: Instant = Instant.now()
)
#Entity
class SubClass(
val someProperty: String = ""
) : BaseEntity()
I'm using the JPA plugin for Gradle, which I believe creates the noarg constructor, that's why I don't have to specify everything nullable.
Thank you!
The logged error has to do with lazy loading.
Hibernate extends entities at runtime to enable it. It is done by intercepting an access to properties when an entity is loaded lazily.
Kotlin has flipped the rules and all classes are final by default there. It is the reason why we're advised to add an open keyword.
If a property is not open hibernate cannot intercept access to it because final methods cannot be overridden. Hence the error.
Why isn't it complaining about the ID?
Because #Id is always loaded. There is no need to intercept access to it.
It seems to work without the open keyword, then why is the error logged?
The key word here is seems. It may introduce subtle bugs.
Consider the following #Entity:
#Entity
public class Book {
#Id
private Long id;
private String title;
public final Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public final String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
And the #Test:
#Test
public void test() {
EntityManager entityManager = entityManagerFactory.createEntityManager();
entityManager.getTransaction().begin();
// signal here
Book book = new Book();
book.setId(1L);
book.setTitle("myTitle");
entityManager.persist(book);
// noise
entityManager.getTransaction().commit();
entityManager.close();
entityManager = entityManagerFactory.createEntityManager();
entityManager.getTransaction().begin();
// signal
Book reference = entityManager.getReference(Book.class, 1L);
String title = reference.getTitle();
assertNull(title); // passes
entityManager.getTransaction().commit();
entityManager.close();
}
This test passes but it should not (and fails if getTitle is not final).
This would be hard to notice
Why do I have to do that in the superclass, and don't need in subclass? I'm not overriding those properties.
Looks like Hibernate gives up when it sees final #Entity.
Add open to SubClass and you will the precious:
2019-05-02 23:27:27.500 ERROR 5609 --- [ main] o.h.tuple.entity.PojoEntityTuplizer : HHH000112: Getters of lazy classes cannot be final: com.caco3.hibernateanswer.SubClass.someProperty
See also:
final methods on entity silently breaks lazy proxy loading
How to avoid initializing HibernateProxy when invoking toString() on it? - my old question (note that Hibernate uses Byte Buddy these days).
PS
Did you forget to include #MappedSuperclass on BaseEntity?
Without the annotation it should fail with something like:
org.hibernate.AnnotationException: No identifier specified for entity: com.caco3.hibernateanswer.SubClass
As I stumbled across Encapsulation while learning Java. One of the benefits of using Getters and Setters, according to https://www.tutorialspoint.com/java/java_encapsulation.htm, is:
"The users of a class do not know how the class stores its data. A class can change the data type of a field and users of the class do not need to change any of their code."
But I'm not quite sure what this mean. Does it mean that a:
private String name;
can be changed to:
private int name; ?
I would be appreciate if someone could give an example regarding to this.
Yes, for example if there are only a fixed number of possible names, then maybe the class would change from:
class Thing
{
private String m_name;
....
public String getName()
{
return m_name;
}
...
}
to
class Thing
{
private static String[] POSSIBLE_NAMES = ...
private int m_nameIndex;
....
public String getName()
{
return POSSIBLE_NAMES[m_nameIndex];
}
...
}
The point is that the getter indicates that the object has a name and can tell it to you when you ask, but the object is free to store or calculate that name however it likes.
A class can change the data type of a field
I think this is wrong thing.its not data type its Access Modifiers when we using private access modifiers that variable visible to the class only. the public methods are the access points to this class's fields from the outside java world. Normally these methods are referred as getters and setters. Therefore any class that wants to access the variables should access them through these getters and setters.no need to change private String name; to public String name;
I'm trying to create a URI and returns an object looking for NIF. (One custom filter)
I have tried to replicate the search by id but does not work, the truth, I'm not sure what I do. I have two classes with these functions
ClientesAbstractFacade.java
public T findNif(Object nif) {
return getEntityManager().find(entityClass, nif);
}
lientesFacadeREST.java
#GET
#Path("nif/{nif}")
#Produces({MediaType.APPLICATION_JSON})
public Clientes findNif(#PathParam("nif") String nif) {
return super.findNif(nif);
}
And here POJO
#Entity
#Table(name = "clientes")
public class Clientes implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Integer id;
As you can see, I'm trying to do custom searches, something easy, and then implement a login.
But I can not even filter by nif, these return error 500
java.lang.IllegalArgumentException: You have provided an instance of an incorrect PK class for this find operation. Class expected : class java.lang.Integer, Class received : class java.lang.String.
The exception says it all:
IllegalArgumentException: You have provided an instance of an incorrect PK class for this find operation. Class expected : class java.lang.Integer, Class received : class java.lang.String
The getEntityManager().find(entityClass, nif) is working on the Primary Key column of your Table. This is, as the exception states, an Integer.
I guess you want to use your NamedQueries and have thus to use createNamedQuery-methods of the EntityManager.
So your find-method should look something like that:
public T findNif(String nif) {
return (T) getEntityManager().createNamedQuery("Clientes.findByNif", Clientes.class)
.setParameter("nif", nif).getSingleResult();
}
I don't like the requirement on have at least one empty constructor and public setters on JPA entities. While I understand the issue on the EntityManager side, this invalidates class invariants.
Does anyone have a solution for this (design pattern or idiom level) ?
Thanks!
Igor
With JPA, the default constructor is required, however, you are not required to use setters. You can choose a property access strategy(field or method) based on where you place the annotations.
The following code will use direct field access and will work as a part of an entity without a setter:
#Column(name = DESCRIPTION)
private String description;
public String getDescription() { return description; }
Versus method access with a setter:
private String description;
#Column(name = DESCRIPTION)
public void setDescription(String description) {
this.description = description;
}
public String getDescription() { return description; }
In point of fact you should have both a no-args constructor and getter and setter methods. The requirements are indicated in section 2.1 of the spec.
The no-arg constructor requirement is found on page 17 in my copy :
The entity class must have a no-arg
constructor. The entity class may have
other constructors as well. The no-arg
constructor must be public or
protected.
Page 18 has the requirement for accessor methods :
The persistent state of an entity is
represented by instance variables,
which may correspond to Java- Beans
properties. An instance variable may
be directly accessed only from within
the methods of the entity by the
entity instance itself. Instance
variables must not be accessed by
clients of the entity. The state of
the entity is available to clients
only through the entity’s accessor
methods (getter/setter methods) or
other business methods. Instance
variables must be private, protected,
or package visibility.
Field vs. Property access indicates how the JPA provider interacts with your entity, not how the client application interacts with it. The client should always use get and set methods.
Some JPA providers are more lenient in these requirements and you may be able to make the constructor private (as suggested above) with a specific vendor. The application might not be portable though so you could be in for a surprise if you migrate in the future.
So I wouldn't recommend omitting the methods entirely. In order to resolve the problem I'd mark the public no-arg ctor as deprecated (put something in the javadoc about it being for JPA provider use only). The set methods can contain the logic you want to maintain your invariants.
It isn't ideal but it should prevent the wrong ctor from being used by accident (I'm assuming you have a ctor that sets the invariants).
OpenJPA can add a no-arg ctor as a part of enhancing your entities.
Just so we're clear, the requirement is mandated in the JPA spec. The previous answer says that you can make the no-arg ctor private, but that is not compliant with the spec(I see the link points to a Hibernate specific page). The spec states that an Entity must have a public or protected no-arg ctor.
-Rick
With DataNucleus you don't have to add a default constructor if you don't want to; it will be added automatically by bytecode enhancement. Also you could persist fields instead of properties, hence no need for public setters.
--Andy (DataNucleus)
Just make your constructor protected or private, so you preserve the class invariants!
public class Person {
private String firstName;
private String lastName;
public Person(String firstName, String lastName) {
setFirstName(firstName);
setLastName(lastName);
}
// private no-arg constructor for hibernate.
private Person() {
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
// private setters for hibernate
private void setFirstName(String nme) {
firstName = nme;
}
private void setLastName(String nme) {
lastName = nme;
}
}
see http://www.javalobby.org/java/forums/m91937279.html for details.
Yes, persist fields instead of properties, but on the not wanting a default constructor you're typically (unless the byte code enhancer does some trick) you're not going to get away from it.
You have to allow your jpa implementation to instantiate the class, so a public default constructor is mandatory.