In Realm database solution how can i get latest inserted item with best performance?
this code is my solution to resolve that
List<Transactions> allItems = realm.where(Transactions.class).findAll().sort("createdAt", Sort.DESCENDING);
String latestId = allItems.get(0).getId();
getUserLatestTransactions(Integer.parseInt(latestId));
is there any other solution or Realm has implement that?
Here is what you could do - considering Realm reads make no copies, meaning it is not expensive and won't affect your UI thread much, after storing your item, you can findAllSorted("createdAt) or findAllSorted("id"), then find last id using last() method like this:
//after commitTransaction,
RealmResults<Transactions> allTransactions = realm.where(Transactions.class).findAllSorted("createdAt");
//If you have an incrementing id column, do this
long lastInsertedId = allTransactions.last().getId();
This should give you the last inserted ID of the given model.
It is also important to mention that for this to work, you MUST have a column in your model like this with id;
public class Transactions extends RealmObject{
#PrimaryKey #Index
private long id;
//getters and setters accordingly!
}
I hope this helps! Good luck and happy coding!
UPDATE
I just realized that realm.copyToRealm(obj) returns an object!
That means you can simply do this:
realm.beginTransaction();
Transactions transaction = realm.copyToRealm(newTransaction);
long id = transaction.getId();
realm.commitTransaction();
Please try this and let me know!
Transactions item = realm.where(Transactions.class).findAll().last()
Note:if you want only get last insert data,no sort method
val obj = realm.where<Transactions>()
.sort("createdAt", Sort.DESCENDING)
.findFirst()
if(obj != null){
val latestId = obj.id
}
If Id is unique & incremental you can sort by it or put the time as long value and do same as above it should work.
Related
The idea is basically to extend some Repositories with custom functionality. So I got this setup, which DOES work!
#MappedSuperclass
abstract class MyBaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Int = 0
var eid: Int = 0
}
interface MyRepository<T : MyBaseEntity> {
#Transactional
fun saveInsert(entity: T): Optional<T>
}
open class MyRepositoryImpl<T : MyBaseEntity> : MyRepository<T> {
#Autowired
private lateinit var entityManager: EntityManager
#Transactional
override fun saveInsert(entity: T): Optional<T> {
// lock table
entityManager.createNativeQuery("LOCK TABLE myTable WRITE").executeUpdate()
// get current max EID
val result = entityManager.createNativeQuery("SELECT MAX(eid) FROM myTable LIMIT 1").singleResult as? Int ?: 0
// set entities EID with incremented result
entity.eid = result + 1
// test if table is locked. sending manually 2-3 POST requests to REST
Thread.sleep(5000)
// save
entityManager.persist(entity)
// unlock
entityManager.createNativeQuery("UNLOCK TABLES").executeUpdate()
return Optional.of(entity)
}
}
How would I do this more spring-Like?
At first, I thought the #Transactional would do the LOCK and UNLOCK stuff. I tried a couple of additional parameters and #Lock. I did go through docs and some tutorials but the abstract technical English is often not easy to understand. At the end, I did not get a working solution so I manually added the table-locking, which works fine. Still would prefer a more spring-like way to do it.
1) There might be a problem with your current design as well. The persist does not instantly INSERT a row in the database. That happens on transaction commit when the method returns.
So you unlock the table before the actual insert:
// save
entityManager.persist(entity) // -> There is no INSERT at this point.
// unlock
entityManager.createNativeQuery("UNLOCK TABLES").executeUpdate()
2) Going back to how to do it only with JPA without natives (it still requires a bit of a workaround as it is not supported by default):
// lock table by loading one existing entity and setting the LockModeType
Entity lockedEntity = entityManager.find(Entity.class, 1, LockModeType.PESSIMISTIC_WRITE);
// get current max EID, TRY NOT TO USE NATIVE QUERY HERE
// set entities EID with incremented result
// save
entityManager.persist(entity)
entityManager.flush() // -> Force an actual INSERT
// unlock by passing the previous entity
entityManager.lock(lockedEntity, LockModeType.NONE)
I'm having trouble getting a field which is in an object which is inside another object. I can get some fields, but others no.
This is the test I created to reproduce this error.
public void commentTest(){
try {
new MyUser("mauri#mail.com","Maurizio Pozzobon","01","facebook","hash").insert();
} catch (Exception e) {}
MyUser user = MyUser.findByEmail("mauri#mail.com");
Place place = new Place(user,"posto","bel posto",null,null);
place.insert();
assertNotNull(user);
Event e =new Event(user,place, "Festa","Questa รจ una gran bella festa",null,new Date(),(long) 10,false,null);
e.insert();
assertNotNull(user.nome);
EventComment ec = new EventComment(user, e, "TestComment", new Date());
ec.insert();
List<EventComment> ecs = e.comments.fetch();
for (EventComment comment : ecs) {
assertNotNull(comment.user.id);
MyUser us= MyUser.findById(comment.user.id);
assertNotNull(us.nome);
assertNotNull(comment.user.nome);
}
}
It fails at the line
assertNotNull(comment.user.nome);
This isn't a deal breaker since I still can get to that field doing other calls to the DB, but it seems weird I can access some fields and others can't
In MyUser I tried both declaring the 'nome' field with and without the following annotations
#Column("nome")
#Max(200) #NotNull
public String nome;
No basically, this is normal.
You use GAE, am I right?
In GAE, remember that there is no JOIN as in SQL DB.
When you fetch a comment, the linked user is not fetched entirely but only the user.id field is filled. That's why assertNotNull(comment.user.id) is OK.
So, by default, if you want the user associated to a comment, you need to fetch it manually.
This limitation should change soon as we are going to provide entity grouping very soon and also a new annotation #Join that will fetch the linked entity(ies) automatically.
You can already try this annotation but it's not yet finalized.
In your comment class, add the #Join :
#Join
#Column("user")
User user;
Then when you fetch one comment, it will also fetch the user with it.
Comment comment = Comment.findById("id", value);
assertNotNull(comment.user.nome); // will be OK.
But it shouldn't work in the case : List<EventComment> ecs = e.comments.fetch();
This join is much more complicated and until we have entity grouping, it would consume too much resources behind the curtain.
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?
I'm having what seems to be a transactional issue in my application. I'm using Java 1.6 and Hibernate 3.2.5.
My application runs a monthly process where it creates billing entries for a every user in the database based on their monthly activity. These billing entries are then used to create Monthly Bill object. The process is:
Get users who have activity in the past month
Create the relevant billing entries for each user
Get the set of billing entries that we've just created
Create a Monthly Bill based on these entries
Everything works fine until Step 3 above. The Billing Entries are correctly created (I can see them in the database if I add a breakpoint after the Billing Entry creation method), but they are not pulled out of the database. As a result, an incorrect Monthly Bill is generated.
If I run the code again (without clearing out the database), new Billing Entries are created and Step 3 pulls out the entries created in the first run (but not the second run). This, to me, is very confusing.
My code looks like the following:
for (User user : usersWithActivities) {
createBillingEntriesForUser(user.getId());
userBillingEntries = getLastMonthsBillingEntriesForUser(user.getId());
createXMLBillForUser(user.getId(), userBillingEntries);
}
The methods called look like the following:
#Transactional
public void createBillingEntriesForUser(Long id) {
UserManager userManager = ManagerFactory.getUserManager();
User user = userManager.getUser(id);
List<AccountEvent> events = getLastMonthsAccountEventsForUser(id);
BillingEntry entry = new BillingEntry();
if (null != events) {
for (AccountEvent event : events) {
if (event.getEventType().equals(EventType.ENABLE)) {
Calendar cal = Calendar.getInstance();
Date eventDate = event.getTimestamp();
cal.setTime(eventDate);
double startDate = cal.get(Calendar.DATE);
double numOfDaysInMonth = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
double numberOfDaysInUse = numOfDaysInMonth - startDate;
double fractionToCharge = numberOfDaysInUse/numOfDaysInMonth;
BigDecimal amount = BigDecimal.valueOf(fractionToCharge * Prices.MONTHLY_COST);
amount.scale();
entry.setAmount(amount);
entry.setUser(user);
entry.setTimestamp(eventDate);
userManager.saveOrUpdate(entry);
}
}
}
}
#Transactional
public Collection<BillingEntry> getLastMonthsBillingEntriesForUser(Long id) {
if (log.isDebugEnabled())
log.debug("Getting all the billing entries for last month for user with ID " + id);
//String queryString = "select billingEntry from BillingEntry as billingEntry where billingEntry>=:firstOfLastMonth and billingEntry.timestamp<:firstOfCurrentMonth and billingEntry.user=:user";
String queryString = "select be from BillingEntry as be join be.user as user where user.id=:id and be.timestamp>=:firstOfLastMonth and be.timestamp<:firstOfCurrentMonth";
//This parameter will be the start of the last month ie. start of billing cycle
SearchParameter firstOfLastMonth = new SearchParameter();
firstOfLastMonth.setTemporalType(TemporalType.DATE);
//this parameter holds the start of the CURRENT month - ie. end of billing cycle
SearchParameter firstOfCurrentMonth = new SearchParameter();
firstOfCurrentMonth.setTemporalType(TemporalType.DATE);
Query query = super.entityManager.createQuery(queryString);
query.setParameter("firstOfCurrentMonth", getFirstOfCurrentMonth());
query.setParameter("firstOfLastMonth", getFirstOfLastMonth());
query.setParameter("id", id);
List<BillingEntry> entries = query.getResultList();
return entries;
}
public MonthlyBill createXMLBillForUser(Long id, Collection<BillingEntry> billingEntries) {
BillingHistoryManager manager = ManagerFactory.getBillingHistoryManager();
UserManager userManager = ManagerFactory.getUserManager();
MonthlyBill mb = new MonthlyBill();
User user = userManager.getUser(id);
mb.setUser(user);
mb.setTimestamp(new Date());
Set<BillingEntry> entries = new HashSet<BillingEntry>();
entries.addAll(billingEntries);
String xml = createXmlForMonthlyBill(user, entries);
mb.setXmlBill(xml);
mb.setBillingEntries(entries);
MonthlyBill bill = (MonthlyBill) manager.saveOrUpdate(mb);
return bill;
}
Help with this issue would be greatly appreciated as its been wracking my brain for weeks now!
Thanks in advance,
Gearoid.
Is your top method also transactional ? If yes most of the time i've encountered that kind of problem, it was a flush that was not done at the right time by hibernate.
Try to add a call to session.flush() at the beginning of the getLastMonthsBillingEntriesForUser method, see if it address your problem.
Call session.flush() AND session.close() before getLastMonthsBillingEntriesForUser gets called.
Please correct my assumptions if they are not correct...
As far as I can tell, the relationship between entry and user is a many to one.
So why is your query doing a "one to many" type join? You should rather make your query:
select be from BillingEntry as be where be.user=:user and be.timestamp >= :firstOfLastMonth and be.timestamp < :firstOfCurrentMonth
And then pass in the User object, not the user id. This query will be a little lighter in that it will not have to fetch the details for the user. i.e. not have to do a select on user.
Unfortunately this is probably not causing your problem, but it's worth fixing nevertheless.
Move the declaration of BillingEntry entry = new BillingEntry(); to within the for loop. That code looks like it's updating one entry over and over again.
I'm guessing here, but what you've coded goes against what I think I know about java persistence and hibernate.
Are you certain that those entries are being persisted properly? In my mind, what is happening is that a new BillingEntry is being created, it is then persisted. At this point the next iteration of the loop simply changes the values of an entry and calls merge. It doesn't look like you're doing anything to create a new BillingEntry after the first time, thus no new id's are generated which is why you can't retrieve them later.
That being said, I'm not convinced the timing of the flush isn't a culprit here either, so I'll wait with bated breathe for the downvotes.
I am writing a website using JSP, JSTL, Servlets and JavaBeans.
At one point of my code, I am trying to use an ArrayList of objects, and a strange thing is happening: when I add the first object it is fine, and when I add a second object it adds it in the second place, but the object at index(0) gets the same values as the object at index(1).
Maybe a problem is in the
ArrayList<Article> articleList = new ArrayList<Article>();
Article newArticle = new Article();
Since articleList is ArrayList of Article class.
Can somebody point me to what I am doing wrong?
Below is my code:
public ArrayList<Article> getArticles()
{
baseIO mySql = new baseIO();
ArrayList<Article> articleList = new ArrayList<Article>();
int articleId = 0;
try
{
String sql =
"select * from jsp_blog_article order by article_id Desc Limit 3";
con = (Connection)mySql.getConnection();
pstmt = (PreparedStatement) con.prepareStatement(sql);
ResultSet rs = pstmt.executeQuery();
while (rs.next()) {
Article newArticle = new Article();
newArticle.setArticleAuthor(rs.getString("article_name"));
newArticle.setArticleBody(rs.getString("article_body"));
newArticle.setArticleAuthor(rs.getString("article_author"));
newArticle.setArticleDate(rs.getString("article_date"));
articleId = Integer.parseInt(rs.getString("article_id"));
newArticle.setArticleId(String.valueOf(articleId));
newArticle.setArticleComments(this.getCommentsNum(articleId));
articleList.add(newArticle);
}
con.close();
pstmt.close();
}
catch(Exception e)
{
return null;
}
return articleList;
}
And the Article class
package objects;
import java.io.Serializable;
public class Article implements Serializable{
private String articleName;
private String articleBody;
private String articleAuthor;
private String articleComments;
private String articleDate;
private String articleId;
public Article()
{
}
// all the getters and setters in place, but it is too long
// so i am not going to post them in forum
}
I would try it this way and see what this does.
int x = 0;
while (rs.next()) {
articleList.add(new Article());
articleList.get(x).setArticleName(rs.getString("article_name"));
articleList.get(x).setArticleBody(rs.getString("article_body"));
articleList.get(x).setArticleAuthor(rs.getString("article_author"));
articleList.get(x).setArticleDate(rs.getString("article_date"));
articleList.get(x).setArticleId(rs.getString("article_id"));
articleList.get(x).setArticleComments(this.getCommentsNum(articleId));
x++;
}
You are calling newArticle.setArticleAuthor twice...I know that's not part of your list problem, but that is an observation.
The code should be cleaned up per the other comments, but functionally, it should work.
Here's what I think is happening.
Your code has the following two lines in it:
newArticle.setArticleAuthor(rs.getString("article_name"));
newArticle.setArticleAuthor(rs.getString("article_author"));
and there is no corresponding call to:
newArticle.setArticleName(rs.getString("article_name"));
this means that your object has no article name specified (even though the author is specified). I'll bet that you are then doing some sort of processing before you display the list that somehow merges articles with the same name.
As a general approach to debugging, I recommend that you mock up your code so you can run it in a debugger and see what's actually going on (right now, your system has so many moving parts that it's going to be difficult for you to hone in on the actual problem).
In the current case, this would be as simple as running the one method outside of your web container, and using a debugger to take a look at the objects in the list that gets returned. You will find that the objects in the list are, indeed, separate objects - just having the same articleName property.
Code looks fine, how are you displaying the list that makes you think the same value is at both indexes? Perhaps your problem is with that code.
Are you adding articles to the database concurrently while reading them? I think that it is possible, depending on your storage engine, that you'd have problems reading while updating is going on.