Custom Iterator in Datastore shows zero using JAVA - java

I used my datastore entity to store an int value number=0. In an add function I used count = number++. But first time app push to GAE, it shows 0. Then it start from 1. So I changed as int number =10 even though I get the value is zero but datastore store as 10. How can I get the updated value in java page after inserting a record? When I try to get the current value, it shows zero but in datastore it store as 10. Please help me to out this problem. how to get value 10. This happens only at the first time of deploy and first ticket only then it shows correct value (iterator value)
thanks
here my code
#Entity
public class Ticket {
//static int nextID = 17;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private Text description;
static int nextID = 10;
private int current;
public Ticket(String title, Text description) {
current = nextID++;
this.title = title;
this.priority = priority;
this.description = description;
}

Google Cloud Platform has multiple datacenters and they do not always hold the same state of the same record. This is especially true if you query for the value immediately after increasing it. You can use ancestor queries to retrieve the Entity, that will make it up to date and consistent. Please look at the article [1] for details.
Here's a link with ancestor query [2]. The idea is that when you use an ancestor query, it forces the query to return data after all changes are finalized (for that query). This ensures up-to-date strong consistency.
[1] - https://cloud.google.com/developers/articles/balancing-strong-and-eventual-consistency-with-google-cloud-datastore/
[2] - https://cloud.google.com/appengine/docs/java/datastore/queries#Java_Ancestor_queries
===---=== Example (from the 2nd link I provided)
DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
Entity tom = new Entity("Person", "Tom");
Key tomKey = tom.getKey();
datastore.put(tom);
Entity weddingPhoto = new Entity("Photo", tomKey);
weddingPhoto.setProperty("imageURL",
"http://domain.com/some/path/to/wedding_photo.jpg");
Entity babyPhoto = new Entity("Photo", tomKey);
babyPhoto.setProperty("imageURL",
"http://domain.com/some/path/to/baby_photo.jpg");
Entity dancePhoto = new Entity("Photo", tomKey);
dancePhoto.setProperty("imageURL",
"http://domain.com/some/path/to/dance_photo.jpg");
Entity campingPhoto = new Entity("Photo");
campingPhoto.setProperty("imageURL",
"http://domain.com/some/path/to/camping_photo.jpg");
List<Entity> photoList = Arrays.asList(weddingPhoto, babyPhoto,
dancePhoto, campingPhoto);
datastore.put(photoList);
Query photoQuery = new Query("Photo")
.setAncestor(tomKey);
// This returns weddingPhoto, babyPhoto, and dancePhoto,
// but not campingPhoto, because tom is not an ancestor
List<Entity> results = datastore.prepare(photoQuery)
.asList(FetchOptions.Builder.withDefaults());
===---===
See these parts:
Entity weddingPhoto = new Entity("Photo", tomKey);
Entity dancePhoto = new Entity("Photo", tomKey);
This generates an Entity with an ancestor key "tomKey".
Now save the Entities into the Datastore:
List<Entity> photoList = Arrays.asList(weddingPhoto, babyPhoto,
dancePhoto, campingPhoto);
datastore.put(photoList);
When you need to fetch the results, perform a special query:
Query photoQuery = new Query("Photo")
.setAncestor(tomKey);
This makes sure the photoQuery isn't just a random query aimed at any random datacenter pulling out any data; it makes sure it fetches the up-to-date data from the datastore.

Related

Unique constraint violated on replace item from list in test

So I have client = creditor which has list of documents. This list can contain only one type of each document, so i have method add document which adds new documnet, but if there is already document of this type it should be replaced.
this test fail on unique constraint
def "should replace documents with same type"() {
given:
def creditor = creditors.create(CreditorHelper.createSampleCreditorForm())
def documentType = DocumentTypeEvent.INVESTMENT_INSTRUCTION
and:
def old = documents.addDocument(new DocumentForm("urlOld", creditor.creditorReference, documentType, ZonedDateTime.now()))
when:
documents.addDocument(new DocumentForm("urlNew", creditor.creditorReference, documentType, ZonedDateTime.now()))
then:
def newResult = documentRepository.findByCreditorReference(creditor.creditorReference)
newResult.size() == 1
newResult.find {
it.url == "urlNew"
}
and:
documentRepository.findByHash(old.hash) == Optional.empty()
}
implementaion is simple replace:
#Transactional
public Document addDocument(final DocumentForm documentForm) {
return creditorRepository.findByCreditorReferenceIgnoreCase(documentForm.getCreditorReference())
.addDocument(new Document(documentForm));
}
above calls:
public Document addDocument(Document newDocument) {
documents.removeIf(existingDocument -> existingDocument.getType() == newDocument.getType());
documents.add(newDocument);
}
entity:
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "creditor_id")
#Builder.Default
private List<Document> documents = new ArrayList<>();
funny is that when I remove unique constraint from flyway test is passing, so it seems like problems with transaction.
I think it might be related to Hibernate's queries ordering during flush time. Because persisting new entities is invoked as first operation by Hibernate's session, you get exception as entity is present in DB during flush time. Turn on show_sql option in Hibernate and try look at logs what is the real order of queries sent to DB.
Also read Vlad's post about ordering: A beginner’s guide to Hibernate flush operation order. You can read code of class EventListenerRegistryImpl as well and see how ordering looks like.

return non-persisting data as part of object

in Hibernate is there a way to return an object or list of objects that have derived data that isn't persisted in the table as part of an object?
for example if you had an object of tweets as follows:
long id;
String message;
long profile_id;
//non-persisted properties
long numLikes;
boolean isLiked;
then you had another object to keep track of who has liked the tweet such as
long id;
long liked_id;
long profile_id;
boolean liked;
how would I (can I) set up the tweet object so that I could see a count of likes inside the tweet object? the query would look something like
Select *, count(likes1.id) as numLikes, isNull(liked1.liked,0) as isLiked from tweets
left join (select id,liked_id from likes where liked_id = tweets.id) as likes1 on tweets.id = likes1 .liked_id
left join (select liked_id,liked from likes where profile_id = :authenticated_User) as liked1 on tweets.id = liked1.liked_id
where.....
is there anyway I can stuff all of this in an object without using addScalar on every property in the tweets object? if not what is the proper way of doing this kind of set-up?
(assuming all properties are named correctly in sql query and data is returned as expected I know there are things in my example that will break.)
My suggestion doing it this way (use it only in case you do not use DTOs):
1) create class TweetRecordFilter to help filter TweetRecord
public class TweetRecordFilter(){
private long tweetId;
private long personId;
private long profileId;
private boolean liked;
//create setters and getters for all attributes mentioned above
}
2) create method for finding TweetRecords (you said they keep track of who like what)
public List<TweetRecord> findTweetRecordByFilter(TweetRecordFilter tweetrecordFilter){
// return found records that have properties that were set to tweetRecordFilter
// I suggest do a select using PreparedStatement
/* here write that code*/
}
3) create method for selecting Tweets
public Tweet findTweetById(int tweetId){
// find tweet that has tweetId and say you have found one, let's call it "foundTweet"
// I suggest do a select using PreparedStatement
/* here write that code*/
// find all tweetRecord that have specific profile_id
TweetRecordFilter tweetRecordFilter = new TweetRecordFilter();
tweetRecordFilter.setProfileId(foundTweet.getProfileId);
tweetRecordFilter.setLiked(true);
List<TweetRecord> tweetRecords = findTweetRecordByFilter(tweetRecordFilter);
// set the number of likes for tweet
if(tweetRecords != null){
foundTweet.setLikesCount(tweetRecords.size());
}
return foundTweet;
}
So all put together in a practical example:
int tweetId = 1;
Tweet tweet = findTweetById(1);
int numberOfLikes = tweet.getNumberOfLikes;
More on preparedStatement you can find here.

Performance OpenJPA query (3000+ records) is slow

I'm using Websphere Application Server 7 with buildin OpenJPA 1.2.3 and an Oracle database. I have the following entity:
#NamedNativeQuery(name=Contract.GIVE_ALL_CONTRACTS,
query="SELECT number, name \n" +
"FROM contracts \n" +
"WHERE startdate <= ?1 \n" +
"AND enddate > ?1",
resultSetMapping = Contract.GIVE_ALL_CONTRACTS_MAPPING)
#SqlResultSetMapping(name = Contract.GIVE_ALL_CONTRACTS_MAPPING,
entities = { #EntityResult(entityClass = Contract.class, fields = {
#FieldResult(name = "number", column = "number"),
#FieldResult(name = "name", column = "name")
})
})
#Entity
public class Contract {
public static final String GIVE_ALL_CONTRACTS = "Contract.giveAllContracts";
public static final String GIVE_ALL_CONTRACTS_MAPPING = "Contract.giveAllContractsMapping";
#Id
private Integer number;
private String name;
public Integer getNumber() {
return number;
}
public String getName() {
return name;
}
}
And the following code to retrieve the contracts:
Query query = entityManager.createNamedQuery(Contract.GIVE_ALL_CONTRACTS);
query.setParameter(1, referenceDate);
List contracts = query.getResultList();
entityManager.clear();
return contracts;
The retrieved contracts are passed to a webservice.
Executing this query in Oracle developer takes around 0,35 seconds for 3608 records.
The call to query.getResultList() takes around 4 seconds.
With a logger in the constuctor of the entity, it logs that there are about 10-20 entities created with the same timestamp. Then 0,015 seconds it does something else. I guess OpenJPA stuff.
Is there a way to speed up OpenJPA? Or is the only solution caching?
Object creation may have its fair share in the performance hit. While running your code in the server, you're not only querying the database but also you allocate memory and create a new Contract object for each row. An expanding heap or garbage collection cycle may count for idle periods that you observed.
I'd suggest you skim through OpenJPA documentation on how to process large results sets.
I suggest you downloading VisualVM and set up a profiling for the packages involved. VisualVM can show the time spent in different methods that will sum up to 0.35sec in your case theoretically. You will be able to analyze the distribution of the total time between your code, OpenJPA and the network IO. This will help you to identify the bottleneck.

How to configure a JDO transaction to simulate creating a database sequence in App Engine Java?

This syntax does not produce unique values across different Fetch Groups:
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private long id;
So I've written the method below to simulate generating a database sequence for my Order model. But what I'm not sure about is what the transactional state needs to be configured as here (isolation level / nontransactional read / nontransactional write / optimistic / etc.):
public long getNextId()
{
PersistenceManager pm = this.getPm();
Transaction tx = pm.currentTransaction();
tx.begin();
long nextId = 0;
Query query = pm.newQuery("select id from orders order by id desc");
query.setRange(0, 1);
try
{
List<Order> results = (List<Order>) query.execute();
if (results.iterator().hasNext())
{
for (Order r : results)
{
nextId = r.getId() + 1;
}
}
else
{
return 0;
}
}
catch (Exception e)
{
return 0;
}
tx.commit();
return nextId;
}
Does the scope of the transaction need to be broader than just this method? In other words, should it also include the insert action for the new Order?
I want to make sure that no two Orders that I insert can have the same id value across the entire application.
IDs generated with IdGeneratorStrategy.SEQUENCE are unique for all entities with the same parent. If your entities are root entities (Eg, no parent), then they will all get unique IDs. What is your use case where you have child entities, but need a unique ID across all of them?
Whats wrong with IdGeneratorStrategy.SEQUENCE ? since GAE/J claims to support it

Hibernate: same generated value in two properties

I want the first to be generated:
#Id
#Column(name = "PRODUCT_ID", unique = true, nullable = false, precision = 12,
scale = 0)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "PROD_GEN")
#BusinessKey
public Long getAId() {
return this.aId;
}
I want the bId to be initially exactly as the aId. One approach is to insert the entity, then get the aId generated by the DB (2nd query) and then update the entity, setting the bId to be equal to aId (3rd query). Is there a way to get the bId to get the same generated value as aId?
Note that afterwards, I want to be able to update bId from my gui.
If the solution is JPA, even better.
Choose your poison:
Option #1
you could annotate bId as org.hibernate.annotations.Generated and use a database trigger on insert (I'm assuming the nextval has already been assigned to AID so we'll assign the curval to BID):
CREATE OR REPLACE TRIGGER "MY_TRIGGER"
before insert on "MYENTITY"
for each row
begin
select "MYENTITY_SEQ".curval into :NEW.BID from dual;
end;
I'm not a big fan of triggers and things that happen behind the scene but this seems to be the easiest option (not the best one for portability though).
Option #2
Create a new entity, persist it, flush the entity manager to get the id assigned, set the aId on bId, merge the entity.
em.getTransaction().begin();
MyEntity e = new MyEntity();
...
em.persist(e);
em.flush();
e.setBId(e.getAId());
em.merge(e);
...
em.getTransaction().commit();
Ugly, but it works.
Option #3
Use callback annotations to set the bId in-memory (until it gets written to the database):
#PostPersist
#PostLoad
public void initialiazeBId() {
if (this.bId == null) {
this.bId = aId;
}
}
This should work if you don't need the id to be written on insert (but in that case, see Option #4).
Option #4
You could actually add some logic in the getter of bId instead of using callbacks:
public Long getBId() {
if (this.bId == null) {
return this.aId;
}
return this.bId;
}
Again, this will work if you don't need the id to be persisted in the database on insert.
If you use JPA, after inserting the new A the id should be set to the generated value, i tought (maybe it depends on which jpa provider you use), so no 2nd query needed. then set bld to ald value in your DAO?

Categories

Resources