Modifying property names in JPA queries - java

I'm using a convention of prefixing field names with an underscore. When I generate annotate entity classes with such fields I am stuck to using the underscore-prefixed property names in queries. I want to avoid that, and be able to do:
#Entity
public class Container {
private String _value;
}
// in a lookup method
executeQuery("from Container where value = ?", value);
Is that possible with JPA in general or Hibernate in particular?
Update: Still trying to remember why, but I need this to be annotated on fields rather than on getters.

You can annotate the getter:
#Entity
public class Container {
private String _value;
#Column
public String getValue()
{
return _value;
}
public void setValue( String value )
{
this._value = value;
}
}

You could perhaps write subclasses of your generated entity classes, which have getter methods on them, and then configure the entity manager to use getter/setter access instead if field access? Then your getters/setters could have any name you liked.

Have a look at NamingStrategy. It would be fairly easy to extend the DefaultNamingStrategy and override the columnName method to strip the first underscore (if it is there).

Related

Persistent Model to Domain Model mapping without exposing domains object attributes

I know this is a common question, but I haven't found another that solves my doubts.
Usually, if the project is small, I've persistence annotations in the same object that represents the domain object. This allows to load the entity from database and keep all the setters private, ensuring any instance is always in a valid state. Something like:
#Entity
class SomeEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String attribute1;
private String attribute2;
private String attribute3;
// ... other attributes
protected SomeEntity() {}
/* Public getters */
public Long getId() { ... }
public String getAttribute1() { ... }
public String getAttribute2() { ... }
/* Expose some behaviour */
public void updateAttributes(String attribute1, String attribute2) {
/* do some validations before updating */
}
}
My problem appears if I want to hava a different persistent model. Then I would have something like:
/* SomeEntity without persistent info */
class SomeEntity {
private Long id;
private String attribute1;
private String attribute2;
private String attribute3;
// ... other attributes
protected SomeEntity() {}
/* Public getters */
public Long getId() { ... }
public String getAttribute1() { ... }
public String getAttribute2() { ... }
/* Expose some behaviour */
public void updateAttributes(String attribute1, String attribute2) {
/* do some validations before updating */
}
}
and DAO:
#Entity
class SomeEntityDAO {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String attribute1;
private String attribute2;
private String attribute3;
public SomeEntityDAO() {}
/* All getters and setters */
}
My question is, how can I map SomeEntityDAO to SomeEntity without exposing SomeEntity's attributes?
If I create a constructor like: public SomeEntity(String attribute1, String attribute2, ...) {}, then anyone can create an invalid instance of SomeEntity. The same occurs if I make all setters public in SomeEntity.
I also don't think is a valid solution build the object using updateAttributes() since this will execute some validations I don't whant to execute at this point (we trust the data that's persistet in database).
I'm thinking in having all the setters protected, so the DAO can extend the Entity and have access to setters... but I'm not sure if this is a good option.
Which is the best or common approach to solve this problem?
I've had the same kind of problem. And looking around I've found no solution. Believe me, if it exists is well hidden somewhere. None that suggests what to do when you have to deal with an old project where ORM entities are everywhere and there's a big step between Domain and ORM model.
Given this, I've deducted that if you really want to keep your Domain entities pure (so non get and set - the latter I would NEVER accept!) you have to do some deals. Because there's no way to share the internals without giving the entities some extra knowledge. Beware, this doesn't mean that you have to make the Domain entities aware of the ORM layer, nor that you have to use getters. Just, what I've concluded, the Domain entities should have ways to expose them as a different model.
So, in conclusion, what I would do in your situation is to build up a Visitor pattern. The Domain entity EntityA would implement the EntityAVisitable interface to accept a EntityAVisitor or something like this.
interface EntityAVisitable {
accepts(EntityAVisitor visitor);
}
The builder implements the interface required by the Visitor, EntityAVisitor.
interface EntityAVisitor<T>{
setCombinedValue1_2(String attribute1_attribute2_combinedInEntity);
<T> build();
}
The build() function of the interface EntityAVisitor uses a generic type T. In this way the Domain entity is agnostic about the return type of the concrete implementation of the EntityAVisitor.
Is it perfect? No.
Perfect solution would be to get rid of the ORM (actually I would say that I hate them, because the way are used is most of the times wrong - but this is my personal thought).
Is it nice? No.
A nice solution is not allowed due to language restrictions (I suppose you use Java).
Does it a good work in encapsulating the real content of your Domain entity? Yes.
Not only, in this way you can decide exactly what could be exposed and how. So, in my opinion, is a good deal between keeping the entity pure and having to work with an ORM under the seat.
Domain entity should be self-validating meaning it should only validate itself based on it's internal values. If update requires validation that depends on external dependencies, then I would create an updater class that is responsible for the update. From the updater class, you can use specification pattern (as an injectable dependency) to implement the validation.
Use domain entities when modifying, and DTOs for read-only projections. There are performance and simplification gains when you use straight DTOs in read-only. This is used in CQRS patterns.
class SomeEntity {
private Long id;
private String attribute1;
private String attribute2;
private String attribute3;
// ... other attributes
public SomeEntity() {}
/* Public getters/setter */
public Long getId() { ... }
public String getAttribute1() { ... }
public String getAttribute2() { ... }
public Long setId() { ... }
public String setAttribute1() { ... }
public String setAttribute2() { ... }
}
//classes/interfaces named for clarity
class EntityUpdater implements IEntityUpdater {
public EntityUpdater (ISpecification spec){
}
public updateEntity(SomeEntity entity){
//assert/execute validation
}
}
Some ORMs allow setting entity values through field access (as opposed to setter methods).
JPA uses the #Access annotation. See What is the purpose of AccessType.FIELD, AccessType.PROPERTY and #Access
I created an ORM, sormula, that can use field access. See #Row fieldAccess and test case org.sormula.tests.fieldaccess.

Spring/JPA/persistence entity attribute field cannot be final?

I have a Spring MVC project using JPA which I have worked on for some time in the past without this issue. But now for some reason (likely an environmental issue as I have switch to a new laptop since I last worked on it) I am getting this weird error.
The project is essentially a tool for creating and performing surveys which are just a set of questions. There are multiple types of question such as "auto complete question", "multiple choice question", "integer question", etc which collect different types of data. Each of this question types is modeled by a subclass which extends an abstract class called DdmQuestion which looks something like this:
#Entity
#Table(name = "ddm_question")
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(discriminatorType = DiscriminatorType.STRING, name = "question_type")
#JsonIgnoreProperties({"dataType"})
#JsonTypeInfo(use = JsonTypeInfo.Id.MINIMAL_CLASS, include = JsonTypeInfo.As.PROPERTY, property = "#question_type")
#JsonSubTypes(value = { #Type(DdmTextQuestion.class),#Type(DdmDateQuestion.class),#Type(DdmTimeQuestion.class),#Type(DdmNumberIntegerQuestion.class),#Type(DdmChoiceMultiQuestion.class),#Type(DdmAutoCompleteQuestion.class) })
public abstract class DdmQuestion {
#Id
#GeneratedValue
#Column(name = "question_id")
private int questionId;
#Column(name = "name")
private String name;
public int getQuestionId() {
return questionId;
}
public void setQuestionId(int questionId) {
this.questionId = questionId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#JsonIgnore
public abstract String getDataType();
}
Note the getDataType() method.
Then, for each question type, I have a subclass extending this which looks something like this:
#Entity
#DiscriminatorValue("ddm_question_date")
public class DdmDateQuestion extends DdmQuestion {
final private String DATA_TYPE = "Long"; // this is the line with the error
#Override
public String getDataType() {
return DATA_TYPE;
}
}
Now, I've never encountered this error before (that I can recall) but Eclipse is throwing up an error here that says:
"The Java field for attribute "DATA_TYPE" is final". That's all it
says.
If I remove the #Entity annotation from the class, this error disappears so evidently something in JPA doesn't like something about this but I never had this error before so I'm thinking something changed in a newer version. My POM is not particularly explicit with dependency versions so this would not be surprising.
Can anyone explain to me why this is happening and what the correct resolution is? I could just remove the "final" from the field declaration but this seems wrong to me as it is definitely a final value...
Thanks in advance.
If it is a field that should not be persisted in the database you usually should take advantage of the transient annotation which would tell the persistence provider to ommit that field in its processing.:
#Transient
final private String DATA_TYPE = "Long";
If Eclipse is smart enough, it should stop highlighting the error altogether.
in this linkshttp://docs.oracle.com/javaee/5/tutorial/doc/bnbqa.html#Entities;
An entity class must follow these requirements:
The class must be annotated with the javax.persistence.Entity annotation.
The class must have a public or protected, no-argument constructor. The class may have other constructors.
The class must not be declared final. No methods or persistent instance variables must be declared final.
If an entity instance be passed by value as a detached object, such as through a session bean’s remote business interface, the class must implement the Serializable interface.
Entities may extend both entity and non-entity classes, and non-entity classes may extend entity classes.
Persistent instance variables must be declared private, protected, or package-private, and can only be accessed directly by the entity class’s methods. Clients must access the entity’s state through accessor or business methods.

Set property in an entity when it is initialized using hibernate

I want to set a property when an entity is initialized on the first time. When the uid is set first, the property uuid should also be set from the same id but as a string.
I used this code thinking that the setUid(Long value) method would also set uuid attribute when the entity class is initialized.
#Entity
public class LogUser {
#Transient
private String uuid;
#Id
private Long uid;
public Long getUid() {
return uid;
}
public void setUid(Long value) {
this.uid = value;
this.uuid = value + "";
}
}
But this doesn't seems to work. Is there any way to make this work?
When your Hibernate/JPA annotations are on the fields, Hibernate completely ignores your accessor methods and directly modifies the fields. The easy fix, then, is to move those annotations to the accessor methods (they go on the getters). This will make Hibernate use your setter, triggering its side effect. However, you have to do this for every field in the class - you can't mix field and accessor use within a class - and this may cause problems if you ever put logic in your accessors that you don't want Hibernate to trigger.
Another way would be to null check and lazily initialize in the getter specifically for the transient field, like this:
public String getUuid() {
if (uuid == null) {
uuid = uid + "";
}
return uuid;
}
Finally, if you want to keep Hibernate on field access and insist that it must be initialized eagerly, you could look into making a custom Tuplizer for the class. You'd probably want a subclass of PojoEntityTuplizer, overriding afterInitialize(). Use the Tuplizer annotation on the entity class to associate your custom tuplizer with it.
Why don't you juste use a constructor where you define :
public LogUser(long uid){
this.uid = uid;
this.uuid = uid.toString();
}
Or maybe I didn't understand ?

Because of Hibernate Mapping need to have some of the fields as #Transient but JSP does not have access to them

In Java, I have access to value of Transient fields of the class. However, I do not access to the same fields on JSP. How can I make them available to JSP?
I am retrieving the values using Hibernate, I reckon a solution to this would be to Transformers.aliasToBean option but is there any other solution to it?
Is there anyway to get rid of transient annotation but have the same mapping in Hibernate? In that case, the problem will be solved.
#AssociationOverrides({
#AssociationOverride(name = "tta.names", joinColumns = #JoinColumn(name = "id"))})
public class Names implements java.io.Serializable {
private static final long serialVersionUID = -30956546435023625398L;
#EmbeddedId
private TableToAssociate tta = new TableToAssociate();
#Transient
public String getid() {
return tta.getMyIds().getId();
}
public void setid(String id) {
this.tta.getMyIds().setId(id);
}
In Java, I can access them using following code
System.out.println(mystudents.getNames().iterator().next().getId());
In JSP, I do not have access to them!
<c:forEach var="nm"items="${mystudents.names}">
${nm.id}
</c:forEach>
If I put another field of names that is not transient, JSP successfully show the value of that item.
Try renaming the methods to match the JavaBean specification.
Instead of:
#Transient
public String getid() {
return tta.getMyIds().getId();
}
public void setid(String id) {
this.tta.getMyIds().setId(id);
}
you should have:
#Transient
public String getId() {
return tta.getMyIds().getId();
}
public void setId(String id) {
this.tta.getMyIds().setId(id);
}
Get rid of #Transient on your entity. Based on your embedded id, you've chosen field annotations. You should be able to have a getter that Hibernate won't try to persist without explicitly marking it as such. And change the getter/setter to use correct JavaBean syntax. getId instead of getid.
It is happening because transient keyword stops the field to be serialized. To pass an object to JSP, it must be serialized. This field will not be there in your serialized object and hence, it is not available to JSP.
For solving your problem, you should see James' comment to use #Transient annotation. It is supplied by JPA and it should allow you to NOT save your value in DB, but serialize it so your JSP can use it.
What you are asking is not possible.. As smarter then me said before.
#Transient is just saying not to serialize/deserialize. Hibernate doesn't serialize this and the same is what jsp bean.
You can do several things:
I think the best thing here is to wrap field in getter and setter in the bean.. This way you will set the property on the bean and in case of only one field every other solution will be overhead.
public class Bean() {
private Names name;
private String id;
public Bean() {
//few action to load name
this.id = name.getid();
}
public String getId() {
return this.id;
}
Create different entity for ui(Bean) and DB(Hibernate). In many cases ui layer and entities are not the same and sometime should not be coupled
You can use inheritance and create diffrent implemenation in the hibernate entity and on ui layer(Jsp bean)
If you ask me i think the best is to do what on section 1. If this happen more oftain you should consider decouple your ui layer and business entity.

Assign custom identifier to an #id property

I'm migrating a legacy system over to use Hibernate 3. It currently generates its own identifiers. To keep with what the system currently does before I try and move it over to something a little better, how would I go about specifying (using annotations) my own class that will return the custom generated identifiers when an insert occurs?
Something like:
#Id
#CustomIdGenerator(Foo.class) // obviously this is not a real annotation
public String getId() { ... }
Where the Foo class has one method that generates the identifier.
Currently I'm just calling the setId(String id) method manually but was hoping for a better way to deal with this situation.
I don't think there is out-of-box support for generating custom Ids using custom annotations using pure JPA-2 API. But if you want to use provider specific API, then the job is pretty simple. Sample Example
To be provider independent try any of following tricks....
IdGeneratorHolder
public abstract class IdGeneratorHolder {
/* PersistentEntity is a marker interface */
public static IdGenerator getIdGenerator(Class<? extends PersistentEntity> entityType) {
/* sample impelementation */
if(Product.class.isAssignableFrom(entityType)) {
return new ProductIdGenerator();
}
return null;
}
}
General IdGenerator interface
public interface IdGenerator {
String generate();
}
Specific IdGenerator - Product Id Generator
public class ProductIdGenerator implements IdGenerator {
public String generate() {
/* some complicated logic goes here */
return ${generatedId};
}
}
Now set the generated id either in no-arg constructor OR in #PrePersist method.
Product.java
public class Product implements PersistentEntity {
private String id;
public Product() {
id = IdGeneratorHolder.getIdGenerator(getClass()).generate();
}
#PrePersist
public void generateId() {
id = IdGeneratorHolder.getIdGenerator(getClass()).generate();
}
}
In above example all the ids are of the same type i.e. java.lang.String. If the persistent entities have ids of different types.....
IdGenerator.java
public interface IdGenerator {
CustomId generate();
}
CustomId.java
public class CustomId {
private Object id;
public CustomId(Object id) {
this.id = id;
}
public String toString() {
return id.toString();
}
public Long toLong() {
return Long.valueOf(id.toString());
}
}
Item.java
#PrePersist
public void generateId() {
id = IdGeneratorHolder.getIdGenerator(getClass()).generate().toLong();
}
You can also use your custom annotation...
CustomIdGenerator.java
public #interface CustomIdGenerator {
IdStrategy strategy();
}
IdStrategy.java
enum IdStrategy {
uuid, humanReadable,
}
IdGeneratorHolder.java
public abstract class IdGeneratorHolder {
public static IdGenerator getIdGenerator(Class<? extends PersistentEntity> entityType) {
try { // again sample implementation
Method method = entityType.getMethod("idMethod");
CustomIdGenerator gen = method.getAnnotation(CustomIdGenerator.class);
IdStrategy strategy = gen.strategy();
return new ProductIdGenerator(strategy);
}
One more thing.... If we set id in #PrePersist method, the equals() method cannot rely on id field (i.e. surrogate key), we have to use business/natural key to implement equals() method. But if we set id field to some unique value (uuid or "app-uid" unique within application) in no-arg constructor, it helps us to implement the equals() method.
public boolean equals(Object obj) {
if(obj instanceof Product) {
Product that = (Product) obj;
return this.id ==that.id;
}
return false;
}
If we or someone else call (intentionally or by mistake) the #PrePersist annotated method more than one times, the "unique id will be changed!!!" So setting id in no-arg constructor is preferable. OR to address this issue put a not null check...
#PrePersist
public void generateId() {
if(id != null)
id = IdGeneratorHolder.getIdGenerator(getClass()).generate();
}
}
UPDATE
If we put the id generation in a
no-arg constructor, wouldn't that
cause a problem when loading entities
from the database? because hibernate
will call the no-arg constructor
causing existing ids to be
re-generated
Yeah you are right, I missed that part. :( Actually, I wanted to tell you that:- in my application every Entity object is associated with an Organization Entity; so I've created an abstract super class with two constructors, and every Entity (except Organization) extends this class.
protected PersistentEntityImpl() {
}
protected PersistentEntityImpl(Organization organization) {
String entityId = UUIDGenerator.generate();
String organizationId = organization.getEntityId();
identifier = new EntityIdentifier(entityId, organizationId);
}
The no-arg constructor is for JPA provider, we never invoke no-arg constructor, but the other organization based constructor. As you can see. id is assigned in Organization based constructor. (I really missed this point while writing the answer, sorry for that).
See if you can implement this or similar strategy in your application.
The second option was using the
#PrePersist annotation. I put that in
and the method never got hit and gave
me an exception stating that I needed
to set the id manually. Is there
something else I should be doing?
Ideally, JPA provider should invoke #PrePersist methods (one declared in class and also all the other methods that are declared in super-classes) before persisting the entity object. Can't tell you what is wrong, unless you show some code and console.
You can.
First, implement org.hibernate.id.IdentifierGenerator
Then you'd have to map it in a mapping xml file. I couldn't find a way to do this with annotations:
<!--
<identifier-generator.../> allows customized short-naming
of IdentifierGenerator implementations.
-->
<!ELEMENT identifier-generator EMPTY>
<!ATTLIST identifier-generator name CDATA #REQUIRED>
<!ATTLIST identifier-generator class CDATA #REQUIRED>
Finally, use #GeneratedValue(generator="identifier-name")
Note that this is hibernate-specific (not JPA)
Update: I took a look at the sources of Hibernate, and it seems at one place, after failing to resolve the short name, hibernates attempts to call Class.forName(..). The parameter there is called strategy. So Here's what you try:
try setting the class fully-qualified name as string in the generator attribute
try setting the class fqn as string in the #GenericGenerator strategy attribute (with some arbitrary name)
Let me know which (if any) worked

Categories

Resources