JPA, do not want insertable=false, updatable=false, workaround? - java

I am new to both stackoverflow and JPA so I will try to explain this the best i can.
In an entity I want to set the foreign key by giving the int value but also I want to set it by giving an object. Here is some code to explain it better.
#Entity
public class Thread implements Serializable {
#ManyToOne
#JoinColumn(name = "accountId", referencedColumnName = "id", nullable = false)
public Account getAccount() {
return account;
}
#Column(name = "accountId")
#Basic
public int getAccountId() {
return accountId;
}
}
I have tried several ways but the code above is the best example for what I am trying to achieve. I understand that setting insert = false and update = false, in either of the 2 methods, makes this code work as far as compiling and running. But I want to be able to insert the accountId by using an Account object AND by setting the actual int accountId.
The reason for this is because sometimes, in my server, I only have the accountId and sometimes I have the Account object.
I also understand that the best solution is probably to use account.getId() when creating the Thread and setting the accountId. But it would be logically nice in my server to be able to just use the object.
Thanks in advance!

I think you have hit a conceptual problem in your application. You should stick to set the entity and do not use any foreign key values when using JPA. The cause of the problem is that your application is only providing the accountId at some point.
This may be due to different reasons. If this is because the part of the application only providing the accountId is legacy, than I would think it is perfectly fine to have an adapter that converts the accountId into an Account entity and then set that entity. Also not that the adapter could create a JPA proxy so that no actual database access is required at that point. Another reason I can think of, is that the application is loosing information at some point during processing. This may be the case when the application is using the Account in some place and only hands over it's Id to the code in question. Then such code should be refactored to hand over the entity.
In your specific case you are also able to use both, account as entity and the foreign key as attribute with both being insertable and updatable. You just have to make sure, that the accountId attribute value is consistent with the foreign key pointing to the row represented by the account entity. JPA providers should be able to handle this (I know OpenJPA does for example). However you are a bit restricted with this. For example you are only able to read the accountId attribute value, because setting it to a different value would cause an inconsistency between the account entity value.

Related

External ID Concept?

#Entity
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String name;
private String externalID; //<--- why we need this?
}
Someone has suggested me to include an external Id field in a class something like that? Any suggestions why that could be?
Not sure, what exactly meant by externalID here, since the case of usage is not clear.
But, I assume a couple of cases:
1. External service
External id may be used to map your entity with some id of another resource from different services. Something, that identifies this entity in another system.
For example: in externalID may be stored person twitter id or bank account id.
2. Security-wise
externalID is used to protect (encapsulate) internal id been visible outside, which may cause some security vulnerabilities.
For example:
In your case, internal id is Integer with GenerationType.AUTO, that means, all entities will have an incremental id: 1, 2, 3, ...
Knowing that someone may analyze your API calls and easily iterate through all your accounts via API, e.g: GET api/person/{id}.
Usually, a different type of IDs is used to solve this problem, like UUID, e.g.: 8b9af550-a4c7-4181-b6ba-1a1899109783. Which can be used as externalID in your case.
So, I assume this is the reason to add additional externalID to your entity.
Note: if your Database supports the usage of UUID (or store it as String), you can simply replace your internal id type with UUID and get rid of externalID here.
It is possible that externalID represents the Primary Key of another table that person is relative to. String is quite arbitrary though, you would generally use an Integer, Long, or UUID to represent a primary key. Might need more context in the question.
The purpose behind an external ID is to link your entity with another representation of it from a system that is decoupled from yours.
For example, if you want the store the Facebook ID for SSO reasons, you would do it through a field that could be called externalId, or something like that. Another example might be that you imported some accounts from another database, and you want to store the Primary Key from the source entity that has been imported.
Otherwise, if that field does not represent anything in your business logic, get rid of it.

Is there a way of using a lookup table as a converter in JPA?

Imagine that I have a simple entity as follows:
#Entity
#Table(name = "PERSON")
public class Person {
#Id
#Column(name = "NAME")
private String name;
#Column(name = "GENDER")
private String gender;
}
And two tables, the actual table holding the information and a lookup table.
TABLE PERSON (
NAME VARCHAR2 NOT NULL,
GENDER INT NOT NULL);
TABLE GENDER_LOOKUP (
GENDER_ID INT NOT NULL,
GENDER_NAME VARCHAR2 NOTNULL);
I want to save the information from my entity into the table, so that the String field gender is automatically converted to the corresponding gender int, using the lookup table as a reference. I thought of two approaches, but I was wondering if there was a more efficient way.
Create an enum and use ordinal enum to persist. I would rather avoid this because I'd like to have only one "source of truth" for the information and for various business reasons, it has to be a lookup table.
Use the #Converter annotation and write a custom converter. I think that this would require me to query the table to pull out the relevant row, so it would mean that I would have to make a JPA call to the database every time something was converted.
I'm currently planning to use 2, but I was wondering if there was any way to do it within the database itself, since I assume using JPA to do all of these operations has a higher cost than if I did everything in the database. Essentially attempt to persist a String gender, and then the database would look at the lookup table and translate it to the correct Id and save it.
I'm specifically using openJpa but hopefully this isn't implementation specific.
Since you seriously considered using enum, it means that GENDER_LOOKUP is static, i.e. the content doesn't change while the program is running.
Because of that, you should use option 2, but have the converter cache/load all the records from GENDER_LOOKUP on the first lookup. That way, you still only have one "source of truth", without the cost of hitting the database on every lookup.
If you need to add a new gender1, you'll just have to restart the app to refresh the cache.
1) These days, who know what new genders will be needed.

Order by Key - Objectify

I am trying to query the App Engine Data Store through Objective in Java.
I have stored some dummy data locally buy I can't achieve to get the result ordered by key.
These are the classes:
Parent Class:
#Entity
public class Parent
{
#Getter
#Setter
#Id
long id;
#Getter
#Setter
String type;
public Parent() {
}
}
Main Class
#Entity
#Cache
#Index
public class MainObject
{
#Getter
#Setter
#Id
long id;
#Getter
#Setter
#Unindex
String url;
#Getter
#Setter
Date date;
#Parent
#Getter
#Setter
Key<Parent> type;
public MainObject() {
}
}
The thing is that I want to get this query:
Key<Parent> parent = Key.create(Parent.class, 1);
MainObjectlastUrl = OfyService.ofy().load().type(MainObject.class).ancestor(parent).order("-key").first().now();
This returns null.
List<MainObject> list = OfyService.ofy().load().type(MainObject.class).ancestor(parent).order("-key").list();
This returns an empty list.
But if I remove the order query, I get all entities.
list = OfyService.ofy().load().type(MainObject.class).ancestor(parent).list();
Any ideas?
I have checked at Objectify web page, but I couldn't find much.
Thanks in advance.
The magic Google field that means key is __key__ (two underscores on each side). This is built in to GAE, so you want order("-__key__").
Objectify could provide an orderKey(boolean) method on query to make this slightly more convenient. If you add it to the issue tracker, I'll implement it.
As of Objectify 5.0.1, you can use orderKey(boolean descending) instead of order("__key__") when sorting by key. See Javadoc at http://static.javadoc.io/com.googlecode.objectify/objectify/5.1.14/com/googlecode/objectify/cmd/SimpleQuery.html#orderKey-boolean-
What you are trying to do is fundamentally wrong. Your desire is to have your query return results ordered by key; the very same thing that uniquely identifies your entity within the datastore. I cannot understand why you would want to do this since the key is derived using the Kind, Id and optionally the parent, if your class has one, as such I can't see how this ordering by key could ever be useful, but I am sure you have your reasons for wanting this. Perhaps you could expand on your question by explaining fully what you're trying to achieve.
Now I will attempt to answer your questions on why your queries aren't returning your desired results and suggest some solutions:
Your first query:
MainObjectlastUrl = OfyService.ofy().load().type(MainObject.class).ancestor(parent).order("-key").first().now();
The reason this query is returning null is because the key property you are passing as the condition to the order method to sort against is not a field of your MainObject entity. It does not exist and will always return null when objectify tries to apply the sort order.
The same applies to your second query. It returns an empty list because there are not entities of type MainObject with a key field. The only difference to the first query is that you are specifically requesting a list of entities rather than calling first().
The third query
list = OfyService.ofy().load().type(MainObject.class).ancestor(parent).list();
This query works, of course, because you are querying for all entities of type MainObject that are ancestors of specified `parent' entity. Since such entities exist the query returns the expected results.
As you can see, the assumption that an entity's "key" somehow intrinsically exists as a property of your entity is incorrect. In order to use it sort by Key you would need to add, say a property key to your entity 'MainObject' to hold the value of entity's generated key, which would not make sense and definitely not recommended.
Caveat: there may be a way of getting hold of the key since we know it exists but I am not aware. Perhaps some datastore expert can shed light on this.
I suggest you sort using the indexed properties on your class which make sense within the domain of your application. For example sort by id, since it isn't auto-generated and is likely to have some meaning; ditto for the date property as they're both likely to have some domain value, as opposed to the key. Hope this helps!

Grails. Hibernate lazy loading multiple objects

I'm having difficulties with proxied objects in Grails.
Assuming I've got the following
class Order {
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name="xxx", joinColumns = {#JoinColumn(name = "xxx")}, inverseJoinColumns = {#JoinColumn(name = "yyy")})
#OrderBy("id")
#Fetch(FetchMode.SUBSELECT)
private List<OrderItem> items;
}
class Customer {
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = true)
#JoinColumn(name = "xxx",insertable = false, nullable = false)
private OrderItem lastItem;
private Long lastOrderId;
}
And inside some controller class
//this all happens during one hibernate session.
def currentCustomer = Customer.findById(id)
//at this point currentCustomer.lastItem is a javassist proxy
def lastOrder = Order.findById(current.lastOrderId)
//lastOrder.items is a proxy
//Some sample actions to initialise collections
lastOrder.items.each { println "${it.id}"}
After the iteration lastOrder.items still contains a proxy of currentCustomer.lastItem. For example if there are 4 items in the lastOrder.items collection, it looks like this:
object
object
javassist proxy (all fields are null including id field). This is the same object as in currentCustomer.lastItem.
object
Furthermore, this proxy object has all properties set to null and it's not initialized when getters are invoked. I have to manually call GrailsHibernateUtils.unwrapIdProxy() on every single element inside lastOrder.items to ensure that there are no proxies inside (which basically leads to EAGER fetching).
This one proxy object leads to some really weird Exceptions, which are difficult to track on testing phase.
Interesting fact: if I change the ordering of the operations (load the order first and the customer second) every element inside lastOrder.items is initialized.
The question is: Is there a way to tell Hibernate that it should initialize the collections when they are touched, no matter if any elements from the collection is already proxied in the session?
I think what's happening here is an interesting interaction between the first level cache (stored in Hibernate's Session instance) and having different FetchType on related objects.
When you load Customer, it gets put in to the Session cache, along with any objects that are loaded with it. This includes a proxy object for the OrderItem object, because you've got FetchType.LAZY. Hibernate only allows one instance to be associated with any particular ID, so any further operations that would be acting on the OrderItem with that ID would always be using that proxy. If you asked the same Session to get that particular OrderItem in another way, as you are by loading an Order containing it, that Order would have the proxy, because of Session-level identity rules.
That's why it 'works' when you reverse the order. Load the Order first, it's collection is FetchType.EAGER, and so it (and the first level cache) have fully realized instances of OrderItem. Now load a Customer which has it's lastItem set to one of the already-loaded OrderItem instances and presto, you have a real OrderItem, not a proxy.
You can see the identity rules documented in the Hibernate manual:
For objects attached to a particular Session... JVM identity for database identity is guaranteed by Hibernate.
All that said, even if you get an OrderItem proxy, it should work fine as long as the associated Session is still active. I wouldn't necessarily expect the proxy ID field to show up as populated in the debugger or similar, simply because the proxy handles things in a 'special' way (ie, it's not a POJO). But it should respond to method calls the same way it's base class would. So if you have an OrderItem.getId() method, it should certainly return the ID when called, and similarly on any other method. Because it's lazily initialized though, some of those calls may require a database query.
It's possible that the only real problem here is simply that it's confusing to have it so that any particular OrderItem could be a proxy or not. Maybe you want to simply change the relationships so that they're either both lazy, or both eager?
For what it's worth, it's a bit odd that you've got the ManyToMany relationship as EAGER and the ManyToOne as LAZY. That's exactly the reverse of the usual settings, so I would at least think about changing it (although I obviously don't know your entire use case). One way to think about it: If an OrderItem is so expensive to fetch completely that it's a problem when querying for Customer, surely it's also too expensive to load all of them at once? Or conversely, if it's cheap enough to load all of them, surely it's cheap enough to just grab it when you get a Customer?
I think you can force eager loading this way or using
def lastOrder = Order.withCriteria(uniqueResult: true) {
eq('id', current.lastOrderId)
items{}
}
or using HQL query with 'fetch all'

Hibernate #OneToOne mapping with a #Where clause

Will this work -
#OneToOne()
#JoinColumn(name = "id", referencedColumnName = "type_id")
#Where(clause = "type_name = OBJECTIVE")
public NoteEntity getObjectiveNote() {
return objectiveNote;
}
This is what I am trying to do - get the record from table note whose type_id is the id of the current object and type_name is OBJECTIVE.
I can't get the above mapping to work. What am I doing wrong here?
This just plain does not work, sorry :( You will need to do it as one to many and live with getting a collection with a single element.
If you really want it to work this way, you can trick hibernate by storing both the foreign key ID and the type_name in a join table and telling it that both columns make up the foreign key.
Actually you can achieve this by specifying #OneToOne without any #Where, but putting #Where on the referenced entity class. I tested this on Hibernate 4.3.11.
This works if you don't care about any entity objects that do not match your #Where.
If you do care about other entities, you can probably create a subclass entity, put #Where on it and join that subclass. But I have not tested this scenario.

Categories

Resources