I'm experiencing a bug which seems to be related to the memcache.
java.lang.IllegalArgumentException: Key is incomplete.
at com.google.appengine.api.datastore.KeyFactory.keyToString(KeyFactory.java:164)
at com.googlecode.objectify.cache.KeyMemcacheService.stringify(KeyMemcacheService.java:62)
at com.googlecode.objectify.cache.KeyMemcacheService.putAll(KeyMemcacheService.java:91)
at com.googlecode.objectify.cache.EntityMemcache.empty(EntityMemcache.java:319)
at com.googlecode.objectify.cache.CachingAsyncDatastoreService$5.trigger(CachingAsyncDatastoreService.java:445)
at com.googlecode.objectify.cache.TriggerFuture.isDone(TriggerFuture.java:89)
at com.googlecode.objectify.cache.TriggerFuture.get(TriggerFuture.java:104)
at com.googlecode.objectify.impl.ResultAdapter.now(ResultAdapter.java:34)
at com.googlecode.objectify.util.ResultWrapper.translate(ResultWrapper.java:22)
at com.googlecode.objectify.util.ResultWrapper.translate(ResultWrapper.java:10)
at com.googlecode.objectify.util.ResultTranslator.nowUncached(ResultTranslator.java:21)
at com.googlecode.objectify.util.ResultCache.now(ResultCache.java:30)
at com.googlecode.objectify.util.ResultWrapper.translate(ResultWrapper.java:22)
at com.googlecode.objectify.util.ResultWrapper.translate(ResultWrapper.java:10)
at com.googlecode.objectify.util.ResultTranslator.nowUncached(ResultTranslator.java:21)
at com.googlecode.objectify.util.ResultCache.now(ResultCache.java:30)
The objects I'm trying to persist are extending the following, and have
#Parent Key someclass;
public abstract class AbstractVO<T> implements iVO<T> {
private static final Logger log = Logger.getLogger(AbstractVO.class.getName());
#Id
private Long id;
#Index
private Date lastModified;
public Long getId() {
return id;
}
public Date getLastModified() {
return lastModified;
}
public void setId(Long id) {
this.id = id;
}
public void setLastModified(Date lastModified) {
this.lastModified = lastModified;
}
public Key<?> getKey() {
return Key.create(this);
}
#OnSave
public void onSaveFunction(){
setLastModified(new Date());
}
#SuppressWarnings("unchecked")
public T save(){
try {
ofy().save().entity(this).now();
} catch (IllegalArgumentException e){
log.log(Level.SEVERE, "boom, key was incomplete", e);
}
return (T) this;
}
public Result<Void> delete(){
return ofy().delete().entity(this);
}
#SuppressWarnings("unchecked")
public T refresh(){
if (getId() != null){
return (T) ofy().load().entity(this).now();
}
else {
return (T) this;
}
}
Logs are showing that the data required to save the entities are what you'd expect them to be:
parent id:5813790675304448
parent key:ag5zfnRlY2gtZXNzZW5jZXJZCxIHQ29tcGFueRiAgIDAyK2fCAwLEghDYW1wYWlnbhiAgICA0pCXCwwLEgpBY3Rpb25UeXBlGICAgICS5-gJDAsSDFNlbGxlckFjdGlvbhiAgICAyvOpCgw
entity id: null
Have anyone experienced this problem before and how could I resolve it? I've written test cases to attempt to reproduce on my dev servers, but they are all passing. It only appears to be a problem on production.
Edit:
I have removed the cache on the affected entities, which resulted in the saving of the entity taking 10 seconds (I'm guessing this is the timeout?) and high contention making it blow up..
I think i have identified the problem, it's simply contention.. I have rewritten much of my application to verify - I'm in the middle of migration now and will let you know
This was indeed the problem, my resolution was to remove the entity from the hierarchy and add the parents as indexed fields.
Related
I am currently working on an application connected to a MongoDB instance. I am having trouble where the 'id' field of my object is not being returned to me within the application but is being returned as null.
The schema has an 'entity' as defined below:
{
"entity_id": String,
"parent": String,
"relevance": boolean
}
I'm querying the collection using the Java Sync Driver (4.4.1) like so:
try {
Entity testDoc = collection.find(eq("entity_id", entity_id)).first();
if (testDoc != null) {
//add entity to a list
}
} catch (Exception e) {
LOGGER.log(Level.SEVERE, "Failed to get Entity", e);
}
For some reason this will give me every field in the object when I query EXCEPT the entity_id. I keep getting this returned as:
entity_id= null
Two things stick out to me. The first being that every other field is a String (originally the Id was a UUID object but I simplified while troubleshooting) and they still return if it's other fields. The second being that there is a whitespace before this null value as if it's being formatted. Other null values return as field=null instead of field= null
I was looking to see if there is some security setting preventing things from being labeled as *_id or *id from being returned but I have found no such instance.
Edit: Here is the Entity Pojo for clarity
public class Entity {
#BsonProperty(value = "entity_id")
private String entityID;
#BsonProperty(value = "parent")
private String parent;
#Deprecated
#BsonProperty(value = "relevance")
private boolean relevance;
public Entity() {}
public Entity(String entityID, String parent, Boolean relevance) {
this.entityID = entityID;
this.parent = parent;
this.relevance = relevance;
}
public String getEntityID() {
return entityID;
}
public void setEntityID(String entityID) {
this.entityID = entityID;
}
public String getParent() {
return parent;
}
public void setParent(String parent) {
this.parent = parent;
}
public boolean isRelevant() {
return relevance;
}
public void relevance(boolean relevance) {
this.relevance = relevance;
}
}
So update for anyone watching this, it appears to have been an issue with my Eclipse IDE.
I reimported the project into IntelliJ Community Edition and rebuilt the Maven project, etc... After doing so, the test cases passed and my entityID returns in the query. Hopefully if anyone else runs into this issue they can do something similar.
I encountered some obstackles with getting results from cosmos db sql api using methods:
.readAllItems(partitionKey, classType)
.readAllItems(partitionKey, options, classType)
Above methods work fine when I have flat structure of class. I mean no subclasses nested inside my model class. Below is class example, which does not work:
public class Root {
public Root(){};
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Subclass getSubclass () {
return subclass;
}
public void setSubclass (Subclass subclass) {
this.subclass= subclass;
}
private String id = "";
private Subclass subclass= new Subclass ();
}
My subclass just contains partitionKey for simplicity.
public class Subclass {
public String getPk() { return pk; }
public void setPk(String pk) {
this.pk = pk;
}
private String pk="";
}
With such "structure" I can easily create items inside container, with proper values of partition key, but I am not able to load them again just using partitionKey value. Here is code snippet for reading them from cosmosdb.
PartitionKey partitionKey = new PartitionKey("properValueWhichWorksWithNoNestedClassesInsideRoot");
CosmosPagedFlux<Root> pagedFluxResponse = container.readAllItems(partitionKey, Root.class);
try {
double totalRequestCharge = pagedFluxResponse.byPage(preferredPageSize).flatMap(fluxResponse -> {
entities.addAll(fluxResponse.getResults());
return Mono.just(fluxResponse.getRequestCharge());
})
.reduce(0.0, (x1, x2) -> x1 + x2)
.block();
logger.info("Query charge: {}", totalRequestCharge);
} catch(Exception err) {
err.printStackTrace();
}
return entities;
I am not quite sure if I am doing sth wrong or it is bug, beacuse I use the same code with .queryItems(query, queryOptions, classType), which also returns CosmosPageFlux instead of .readAllItems and it works perfectly fine.
I use such dependancy:
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-cosmos</artifactId>
<version>LATEST</version>
</dependency>
I also used some specific versions intead of LATEST and it does not work.
I've tested in my side and found some interesting things.
readAllItems is a function which achieved by SqlQuery, so when I debug your situation which put the partition key in a sub property, it will generate a sql like this:
So, obviously it can got any response so that the result of readAllItems returns none. You'd better to use queryItems directly.
I do have two entities that relate each other via a OneToMany-Relation.
Entity 1 is named "Change" and looks like the following
public class Change {
String attribute1;
#Column(name="\"ATTRIBUTE1\"")
public void getAttribute1() {
return this.attribute1;
}
public void setAttribute1(String attribute1) {
this.attribute1 = attribute1;
}
// and 7 more of these....
List<ChangeTask> relatedChangeTasks = new ArrayList<ChangeTask>();
#OneToMany(cascade={PERSIST, MERGE, REFRESH}
#JoinTable(name="CHANGE_CHANGETASK", joinColumns={#JoinColumn(name="CHANGE_ID")}, inverseJoinColumns={#JoinColumn(name="CHANGETASK_ID")})
#JoinColumn(name="\"relatedChangeTask_ID\"" )
public List<ChangeTask> getRelatedChangeTasks() {
return this.relatedChangeTasks;
}
public void setRelatedChangeTasks(List<ChangeTask> relatedChangeTasks) {
this.relatedChangeTasks = relatedChangeTasks;
}
}
Entity 2 is named ChangeTask and extends Change.
public class ChangeTask extends Change {
// some additional attributes...
}
Persisting a new or existing Change record with one ChangeTask added to the "relatedChangeTask" list works just perfect.
Now I have to change the annotation of the 8 attributes from Default to #Lob, so Change now looks like this:
public class Change {
String attribute1;
#Lob
#Column(name="\"ATTRIBUTE1\"")
#Basic(fetch=EAGER)
public String getAttribute1() {
if(fieldHandler != null) {
return (java.lang.String) fieldHandler.readObject(this, "attribute1", attribute1);
}
return attribute1;
}
public void setAttribute1(String attribute1) {
if(fieldHandler != null) {
this.attribute1= (java.lang.String) fieldHandler.writeObject(this, "attribute1", this.attribute1, attribute1);
return;
}
this.attribute1= attribute1;
}
// and 7 more of these....
List<ChangeTask> relatedChangeTasks = new ArrayList<ChangeTask>();
#OneToMany(cascade={PERSIST, MERGE, REFRESH}
#JoinTable(name="CHANGE_CHANGETASK", joinColumns={#JoinColumn(name="CHANGE_ID")}, inverseJoinColumns={#JoinColumn(name="CHANGETASK_ID")})
#JoinColumn(name="\"relatedChangeTask_ID\"" )
public List<ChangeTask> getRelatedChangeTasks() {
return this.relatedChangeTasks;
}
public void setRelatedChangeTasks(List<ChangeTask> relatedChangeTasks) {
this.relatedChangeTasks = relatedChangeTasks;
}
}
Now, when I try to add a given ChangeTask to a Change the persist operation does not fail. But at the end of the Transaction the relation has not been persisted, meaning the relation-table "CHANGE_CHANGETASK" remains empty. When I debug through the whole process, I can see that the list contains one entry before "entityManager.merge()" operation and it still contains one entry after the merge. But it never arrives at the database.
Does anybody have an idea what I'm doing wrong here? As strange as it may sound, it must be something related with the #Lob annotations. As soon as I remove those again from the entity everything works fine.
Thanks in advance.
You wrote
public void getAttribute1() {
That can't be right. I think you mean
public String getAttribute1() {
Additionally you have annotated the setter:
#Column(name="\"ATTRIBUTE1\"")
public void setAttribute1(String attribute1) {
this.attribute1 = attribute1;
}
You have to annotage either the field or the getter.
I need something that seems not so specific but anyway I was unable to come up with nice and sophisticated solution.
Say I have very simple hibernate/jpa entity:
#Entity(name="entity")
public class Type {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
#Column(unique = true, nullable = false)
private String name;
#Column(unique = false, nullable = false)
private boolean defaultType;
}
What i need is to somehow annotate defaultType field so only (and exactly) one persisted entity have this value as true. When new entity get persisted with this defaultType as true, the old one (with defaultType=true) entity has to be altered and its defaultType value changed to false. Also if any entity get changed (its defaultType got changed to true), same rule should apply.
As far I know this can be achieved inside business logic (e.g. in DAO layer), with DB trigger or with hibernates interceptor or event (If there is another way, please let me know). I tried with DAO solution but it's kind of bad solution because it can be bypassed and it is really clumsy for such simple operation. DB triggers can not be added with hibernate/jpa annotations (if I am not mistaken) and i am not sure how to make this functionality with hibernate interceptors/events.
So, what is best solution for this problem?
You need use Callback method in JPA, for example PreUpdate or PostUpdate, for instance:
#Entity
#EntityListeners(com.acme.AlertMonitor.class) // set callback method in another class
public class Account {
Long accountId;
Integer balance;
boolean preferred;
#Id
public Long getAccountId() { ... }
...
public Integer getBalance() { ... }
...
#Transient
public boolean isPreferred() { ... }
...
public void deposit(Integer amount) { ... }
public Integer withdraw(Integer amount) throws NSFException {... }
#PreUpdate // callback method in some class
protected void validateCreate() {
if (getBalance() < MIN_REQUIRED_BALANCE)
throw new AccountException("Insufficient balance to open an
account");
}
#PostUpdate // callback method in some class
protected void adjustPreferredStatus() {
preferred =
(getBalance() >= AccountManager.getPreferredStatusLevel());
}
}
// callback method in another class
public class AlertMonitor {
#PreUpdate // callback method in another class
public void updateAccountAlert(Account acct) {
Alerts.sendMarketingInfo(acct.getAccountId(), acct.getBalance());
}
}
Update: About your question, If I undestand what you want, this code may help you:
#Entity(name="entity")
#EntityListeners(com.yourpackage.TypeListner.class)
public class Type {
...
#Column(unique = false, nullable = false)
private boolean defaultType;
}
public class TypeListner {
pivate static Type objectWithTrue = null;
public void init() { // call this method when application is started
List<Type> results = entityManager
.createQuery("from Type", Type.class)
.getResultList();
for(Type type: results) {
if(type.getDefaultType()) {
objectWithTrue = type;
}
}
}
private void changeDefaultType(Type changed) {
if(changed.getDefaultType()) {
if(changed != objectWithTrue && objectWithTrue != null) {
objectWithTrue.setDefaultType(false);
}
objectWithTrue = changed;
}
}
#PostPresist
public void newType(Type changed) {
changeDefaultType(changed);
}
#PostUpdate
public void updateType(Type changed) {
changeDefaultType(changed);
}
#PreRemove
public void removeType(Type changed) {
if(changed.getDefaultType() && objectWithTrue == changed) {
objectWithTrue = null;
}
}
OR
You can use listner #PreUpdate and #PrePresist and every times overwrite all Type objects without store any variable (it isn't so good for perfomance then first example, but more reliable):
#PreUpdate
void updateType(Type changed) {
if(changed.getDefaultType()
List<Type> results = entityManager
.createQuery("from Type", Type.class)
.getResultList();
for(Type type: results) {
if(changed != type && type.getDefaultType()) {
type.setDefaultType(false);
}
}
}
}
I'm trying to upgrade from Spring Data Neo4J 3 to 4 - I'm using Neo4J 2.2.2.
I use a GraphRepository instance to query the database, fetching back an object.
This object has several relationships, which are not fetched (deliberately, to avoid reading in the entire graph).
In the SDN3 code, simply used the Neo4JTemplate class to perform a fetch call for each relationship I needed to fetch. This worked extremely well.
However, in SDN4 this facility has been removed, and replaced by various implementations of the load() method. It's not clear from the documentation how to achieve what I did in SDN3.
To be clear: if I have a Set of objects in the first class I retrieve, governed by a relationship, I want to retrieve only the objects in that Set, not the entire collection of those objects in the database.
Have I missed something crucial in the upgrade process, or is there a simple way of doing what I'm trying to do?
Adding code:
My entity class:
#NodeEntity
public class File implements MetroNode {
private Long id;
private String fileName;
private SourceState sourceState;
private Set<State> states;
#GraphId
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
#Relationship(type = "HAS_FILE", direction = Relationship.INCOMING)
public SourceState getSourceState() {
return sourceState;
}
public void setSourceState(SourceState sourceState) {
this.sourceState = sourceState;
}
public State addState(MetroNode otherNode, StateStatus status) {
if (this.states == null) {
this.states = new HashSet<State>();
}
State state = new State(this, otherNode, status.toString());
this.states.add(state);
return state;
}
#Relationship(type = "HAS_STATE", direction = Relationship.OUTGOING)
public Set<State> getStates() {
return states;
}
public State getActiveState() {
if (this.states != null) {
for (State state : this.states) {
if (state.isActive()) {
return state;
}
}
}
return null;
}
}
My repository class:
public interface FileRepository extends GraphRepository<File> {
File findByFileName(String fileName);
}
When executing the getActiveState() method I get a null return, because the states Set is empty (hasn't been fetched).
Looking again at my code, I wonder if it's because I'm not using a "native" load method from the repository, but the overloaded version?
SDN 4 allows you to control loading of related entities with the persistence horizon.
Loading an entity with depth 0 will fetch properties of the entity and no related entities.
Depth 1 will fetch the first level of related entities, but not their relations and so on.
Controlling the depth by relationship type is not supported.