For some reason in the following function the Cursor is not reading any marks. I don't know what I am doing wrong and I have been debugging this code for ours. When it runs it says the value of tagid and catid are both -1. No ping pong or pang :(
public String getCategoryNameByLawId(final int lawID){
final String[] categoryName = {"Success"+lawID};
final int[] tagID = {-1};
final int[] categoryID = {-1};
runnable = new Runnable() {
#Override
public void run() {
try {
openToRead();
String Query = "SELECT * from " + Constants.TABLE_LAW_TAG;
Cursor c1 = mSqLiteDatabase.rawQuery(Query, null);
if (c1.moveToFirst()) {
categoryName[0] = "ping";
while (c1.isAfterLast() == false) {
categoryName[0] = "pong";
try {
if (c1.getInt(c1.getColumnIndex(Constants.KEY_LAW_ID)) == lawID) {
int indexTagID = c1.getColumnIndex(Constants.KEY_TAG_ID);
tagID[0] = c1.getInt(indexTagID);
categoryName[0] = "pang";
}
} catch (Exception e) {
categoryName[0] = e.getMessage();
}
c1.moveToNext();
}
}
close();
openToRead();
Query = "SELECT * from " + Constants.TABLE_CATEGORY_TAG;
Cursor c2 = mSqLiteDatabase.rawQuery(Query, null);
if (c2.moveToFirst()) {
while (c2.isAfterLast() == false) {
if (c2.getInt(c2.getColumnIndex(Constants.KEY_TAG_ID)) == tagID[0]) {
int indexCategoryID = c2.getColumnIndex(Constants.KEY_CATEGORY_ID);
categoryID[0] = c2.getInt(indexCategoryID);
}
c2.moveToNext();
}
}
/*
exceptionHandler.alert(new RuntimeException(), "catid-" + categoryID[0]);
Query = "SELECT * from " + Constants.TABLE_CATEGORY;
Cursor c3 = mSqLiteDatabase.rawQuery(Query, null);
if (c3.moveToFirst()) {
while (c3.isAfterLast() == false) {
if (c3.getInt(c3.getColumnIndex(Constants.KEY_CATEGORY_ID)) == categoryID[0]) {
int indexCategoryName = c3.getColumnIndex(Constants.KEY_CATEGORY_NAME);
categoryName[0] = c3.getString(indexCategoryName);
}
c3.moveToNext();
}
}
exceptionHandler.alert(new RuntimeException(), "catnam-" + categoryName[0]);*/
close();
}
catch(Exception e){
categoryName[0] ="error";
}
}
};
new Thread(runnable).start();
return categoryName[0].toLowerCase() + " tagid: "+ tagID[0]+ " catid: "+ categoryID[0];
}
start() just starts the thread; it does not wait for it to finish.
The problem is that the value has not yet been found when you're executing return categoryName[0]....
Drop the Runnable indirection, and execute the database code directly.
If you really want to use a thread, you could wait for the thread to finish (call its join()), but this would not make sense because your main thread would be suspended as long as the database code is running. To be able to do other stuff in the main thread, you would have to reorganize your program so that the database code sends a separate message back to the main thread when it has the result. Have it look into CursorLoader (which needs a content provider, though).
Related
my application uses two SwingWorkers, one works in the Homepage to control online users and one works on opening a chat to keep the chat updated.
My problem is that the moment I open the chat (the SwingWorker of the chat starts to work) the SwingWorker of the Homepage no longer works, it is as if it went to standby.
For this reason I tried to "wake up" the thread, but unfortunately I came across the exception that is precisely the title of this article / question.
can any of you help me understand what the problem is?
why is the chat thread blocking the homepage thread?
below you will find the source code (unmodified)
thank you so much in advance for your help!
HOMEPAGE:
private class SWOnlineUserCheck extends SwingWorker<Void, Void>{
#Override
protected Void doInBackground() throws Exception {
while(running){
sleep(3000);
sql = "SELECT USRID,ONLINE FROM USR ORDER BY USRID ASC";
res = db_conn.select(sql);
chatItem = MLP_CHATLIST.getComponents();
while(res.next()){
for(int i=0; i<chatItem.length; i++){
chatItemListStat = (ChatItemList) chatItem[i];
if(chatItemListStat.getUserId() == res.getInt("USRID")){
sql = "SELECT DESCRIPTION FROM KWS WHERE KEYFIELD = 'ONLINESTATUS' AND KEYWORD = '"+res.getString("ONLINE")+"'";
res2 = db_conn.select(sql);
if(res2.next()){
chatItemListStat.ML_STATUS.setText(res2.getString("DESCRIPTION"));
}
if("J".equals(res.getString("ONLINE"))){
chatItemListStat.ML_IMGSTATUS.setIcon(new javax.swing.ImageIcon(getClass().getResource("/OBMS_icons/icons8-green-circle-18.png")));
}
else if("P".equals(res.getString("ONLINE"))){
chatItemListStat.ML_IMGSTATUS.setIcon(new javax.swing.ImageIcon(getClass().getResource("/OBMS_icons/icons8-orange-circle-18.png")));
}
else{
chatItemListStat.ML_IMGSTATUS.setIcon(new javax.swing.ImageIcon(getClass().getResource("/OBMS_icons/icons8-red-circle-18.png")));
}
}
}
}
}
return null;
}
#Override
protected void done(){
}
}
hier i try to wake up the thread of the Homepage:
private void MC_STATUSItemStateChanged(java.awt.event.ItemEvent evt) {
synchronized(this) {
if(running && swonlineusercheck.isDone()){
swonlineusercheck.notify();
}
}
for(var entry:map_status.entrySet()){
if(entry.getValue().equals(MC_STATUS.getItemAt(MC_STATUS.getSelectedIndex()))){
mc_status = entry.getKey();
break;
}
}
sql = "UPDATE USR SET ONLINE = '"+mc_status+"' WHERE USRID = '"+user.getUserId()+"'";
try{
db_conn.update(sql);
}
catch(SQLException ex){
Logger.getLogger(OBMS_HOMEPAGE.class.getName()).log(Level.SEVERE, null, ex);
}
if("J".equals(mc_status)){
ML_STATIMG.setIcon(new javax.swing.ImageIcon(getClass().getResource("/OBMS_icons/icons8-green-circle-18.png")));
}
else if("P".equals(mc_status)){
ML_STATIMG.setIcon(new javax.swing.ImageIcon(getClass().getResource("/OBMS_icons/icons8-orange-circle-18.png")));
}
else{
ML_STATIMG.setIcon(new javax.swing.ImageIcon(getClass().getResource("/OBMS_icons/icons8-red-circle-18.png")));
}
}
CHAT_WINDOW:
private class SWCHAT_WINDOWCheck extends SwingWorker<Void, Void>{
#Override
protected Void doInBackground() throws Exception {
while(running){
if(firstChatQuery){
sql="SELECT * FROM CHT WHERE SENDER IN ('"+chat_sender.getUserId()+"','"+chat_partner.getUserId()+"') "
+ "AND RECEIVER IN ('"+chat_sender.getUserId()+"','"+chat_partner.getUserId()+"') "
+ "AND CAST(DATE AS DATE) = CURRENT_DATE() "
+ "AND CAST(DATE AS TIME) > '"+chatTime+"' ORDER BY DATE ASC";
res = db_conn.select(sql);
}
else{
sql="SELECT * FROM CHT WHERE SENDER IN ('"+chat_sender.getUserId()+"','"+chat_partner.getUserId()+"') "
+ "AND RECEIVER IN ('"+chat_sender.getUserId()+"','"+chat_partner.getUserId()+"') "
+ "AND CHTID > "+lastChatId+" ORDER BY DATE DESC LIMIT 1";
res = db_conn.select(sql);
}
fillChat(res);
}
return null;
}
#Override
protected void done(){
}
}
hier i try to wake up the thread of the Chat window:
private void ChatWrite() throws SQLException{
synchronized(this) {
if(swchatwindowcheck.isDone()){
swchatwindowcheck.notify();
}
}
usrid_sender = chat_sender.getUserId();
usrid_receiver = chat_partner.getUserId();
usrid_writer = usrid_sender;
sql = "INSERT INTO CHT (CHTID,CHGID,SENDER,RECEIVER,WRITER,DATE,TEXT,IMG_VID) "
+"VALUES (NULL,NULL,'"+usrid_sender+"','"+usrid_receiver+"','"+usrid_writer+"'"
+",CURRENT_TIMESTAMP(),'"+MTA_CHATTEXT.getText()+"','')";
db_conn.update(sql);
MTA_CHATTEXT.setText("");
MTA_CHATTEXT.requestFocus();
}
I have a very curious Error that I cant seem to get my head around.
I need to use a ScheduledExecutorService to pass the Survey Entity I created to be edited and then saved as a new Entity.
public void executeScheduled(Survey eventObject, long interval) {
HashMap<String, String> eventRRules = StringUtils.extractSerialDetails(eventObject.getvCalendarRRule());
long delay = 10000;
ScheduledExecutorService service = Executors.newScheduledThreadPool(1);
Runnable runnable = new Runnable() {
private int counter = 1;
private int executions = Integer.parseInt(eventRRules.get("COUNT"));
Survey survey = eventObject;
public void run() {
String uid = eventObject.getUniqueEventId();
logger.info("SurveyController - executeScheduled - Iteration: " + counter);
String serialUid = null;
if (counter == 1) {
serialUid = uid + "-" + counter;
} else {
serialUid = StringUtils.removeLastAndConcatVar(eventObject.getUniqueEventId(), Integer.toString(counter));
}
if (++counter > executions) {
service.shutdown();
}
survey.setUniqueEventId(serialUid);
try {
executeCreateSurvey(survey);
} catch(Exception e) {
logger.debug("SurveyController - executeScheduled - Exception caught: ");
e.printStackTrace();
}
}
};
service.scheduleAtFixedRate(runnable, delay, interval, TimeUnit.MILLISECONDS);
}
When the executeCreateSurvey(survey) Method is run without the ScheduleExecutorService, it works flawlessly.
Yet when it is executed inside the run() Method, I get the "detached entity passed to persist" Error each time the save(survey) Method is run within the executeCreateSurvey() Method....
The executeCreateSurvey() Method where the .save() Method is called:
public ResponseEntity<?> executeCreateSurvey(Survey eventObject) {
MailService mailService = new MailService(applicationProperties);
Participant eventOwner = participantRepositoryImpl.createOrFindParticipant(eventObject.getEventOwner());
eventObject.setEventOwner(eventOwner);
Survey survey = surveyRepositoryImpl.createSurveyOrFindSurvey((Survey) eventObject);
// Saves additional information if small errors (content
// errors,.. )
// occurs
String warnMessage = "";
List<Participant> participants = new ArrayList<Participant>();
for (Participant participantDetached : eventObject.getParticipants()) {
// Check if participant already exists
Participant participant = participantRepositoryImpl.createOrFindParticipant(participantDetached);
participants.add(participant);
// Only create PartSur if not existing (Update case)
if (partSurRepository.findAllByParticipantAndSurvey(participant, survey).isEmpty()) {
PartSur partSur = new PartSur(participant, survey);
partSurRepository.save(partSur);
try {
mailService.sendRatingInvitationEmail(partSur);
surveyRepository.save(survey);
} catch (Exception e) {
// no special exception for "security" reasons
String errorMessage = "error sending mail for participant: " + e.getMessage() + "\n";
warnMessage += errorMessage;
logger.warn("createSurvey() - " + errorMessage);
}
}
}
// Delete all PartSurs and Answers from removed participants
List<PartSur> partSursForParticipantsRemoved = partSurRepository.findAllByParticipantNotIn(participants);
logger.warn("createSurvey() - participants removed: " + partSursForParticipantsRemoved.size());
partSurRepositoryImpl.invalidatePartSurs(partSursForParticipantsRemoved);
return new ResponseEntity<>("Umfrage wurde angelegt. Warnungen: " + warnMessage, HttpStatus.OK);
}
What could the reason be for this?
I have not been able to find this Problem anywhere so far.
I have a table with > 200,000 rows (and might be a few million).
Sometimes the queries can take a few minutes to return a result.
I need to offer the end user the ability to abort the query and refine it.
To do that, I need to implement a query that can be cancelled or interrupted.
Is there a best practice for this?
The solution I came up with is to perform a series of smaller queries using LIMIT and OFFSET, checking for an interrupt between.
My concern is that repeatedly using LIMIT and OFFSET will result in searching the same data rows over and over, yielding a much slower query. Or does SQLite recognize it can start the next query at the row number where a prior identical search stopped?
public class InterruptableQuery {
public interface QueryResult {
void queryResult(boolean interrupted, List<MyData> results);
}
private QueryResult queryCallBack;
private volatile boolean _interrupted = false;
private volatile boolean _cancelled = false;
private SQLiteDatabase db;
public InterruptableQuery(SQLiteDatabase db, QueryResult qr) {
this.db = db;
queryCallBack = qr;
}
public void interruptQuery() { _interrupted = true; }
public void cancelQuery() { _cancelled = true; }
public void search(final String query) {
Thread searchThread = new Thread() {
#Override
public void run() {
boolean done = false;
int OFFSET = 100;
int offset = 0;
List<MyData> resultsList = new ArrayList<>();
while (!done && !_cancelled && !_interrupted) {
Cursor cursor = db.rawQuery(query + " LIMIT " + OFFSET + " OFFSET " + offset, null);
done = (cursor == null) || (cursor.getCount() < OFFSET);
if (cursor != null) {
if (cursor.moveToFirst()) {
do {
resultsList.add(new MyData(cursor));
} while (cursor.moveToNext());
}
cursor.close();
}
offset += OFFSET;
}
if (!_cancelled)
queryCallBack.queryResult(_interrupted, resultsList);
}
};
searchThread.start();
}
}
I am working in Android application in which I am using ormlite. I am taking my phone book contacts and saving them in my local database, but the problem is that it is taking too much time like for almost 1500 contact it is taking almost 70 seconds.
I searched for the Bulk insert in ormlite, but I can't figure it out how to implement it in my following code.
public static void loadLocalPhoneBookSample(Context ctx) {
try{
ContentResolver contentRes = ctx.getContentResolver();
Cursor cur = null;
String selection = ContactsContract.Contacts.HAS_PHONE_NUMBER;
cur = contentRes.query(ContactsContract.Contacts.CONTENT_URI, PROJECTIONS, selection, null, Phone.DISPLAY_NAME + " ASC");
context = ctx;
if (cur.getCount() > 0) {
// create DB object
MUrgencyDBHelper db = new MUrgencyDBHelper(ctx);
RuntimeExceptionDao<ContactLocal, ?> contactDAO = db.getContactLocalIntDataDao();
UpdateBuilder<ContactLocal, ?> updateDAO = contactDAO.updateBuilder();
try {
updateDAO.updateColumnValue("isUseless", true);
updateDAO.update();
} catch (SQLException e) {
e.printStackTrace();
}finally {
// db.writeUnlock();
}
while (cur.moveToNext()) {
String id = cur.getString(cur.getColumnIndex(ContactsContract.Contacts._ID));
/** read names **/
String displayName = cur.getString(cur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
/** Phone Numbers **/
Cursor pCur = contentRes.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null, ContactsContract.CommonDataKinds.Phone.CONTACT_ID
+ " = ?", new String[] { id }, null);
while (pCur.moveToNext()) {
String number = pCur
.getString(pCur
.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
String formatedNo = number.replaceAll("\\s+", "").replace("+", "00").replace("-", "").trim();
try {
QueryBuilder<ContactLocal, ?> query = contactDAO.queryBuilder();
query.where().eq("mFormatedNumber", number);
ContactLocal contact = query.queryForFirst();
boolean addContact = false, alreadyUpdated = true;
if (contact == null) {
addContact = true;
contact = new ContactLocal();
contact.setFirstName(displayName.trim());
contact.setLastName(displayName.trim());
contact.setContactNumber(formatedNo);
}
// check if this contact was already updated before
if (contact.getContactNumber() == null || contact.getContactNumber().length() == 0) {
contact.setContFirstLastNo(number, displayName, displayName, number);
alreadyUpdated = false;
}
contact.setUseless(false);
// if not updated already, Create/Update
if (addContact) {
contactDAO.create(contact);
} else
contactDAO.update(contact);
}
}
pCur.close();
}
}
}
the problem is that it is taking too much time like for almost 1500 contact it is taking almost 70 seconds
#CarloB has the right answer in terms of doing the mass creates inside the dao. callBatchTasks(...) method. Here's the docs on that subject:
http://ormlite.com/docs/batch
To make things a bit faster, you could also go through and record all of the mFormatedNumber in another List and then query for them using an IN query. Use a raw in query to get back the mFormatedNumber that are already in the database:
results = dao.queryRaw(
"SELECT mFormatedNumber from Contact WHERE mFormatedNumber IN ?",
mFormatedNumberList);
For using raw queries with ORMLite, see:
http://ormlite.com/docs/raw-queries
So then you would make one query to see which of the contacts need to be created and then do all of the inserts from within a batch transaction.
Otherwise you are doing ~3000 synchronous database transactions and 40/sec on an Android device is unfortunately pretty typical.
Here is my revised version (might need a few syntax changes)
public static void loadLocalPhoneBookSample(Context ctx) {
try {
ContentResolver contentRes = ctx.getContentResolver();
String selection = ContactsContract.Contacts.HAS_PHONE_NUMBER;
Cursor cur = contentRes.query(ContactsContract.Contacts.CONTENT_URI, PROJECTIONS, selection, null, Phone.DISPLAY_NAME + " ASC");
context = ctx;
if (cur.getCount() > 0) {
// create DB object
MUrgencyDBHelper db = new MUrgencyDBHelper(ctx);
RuntimeExceptionDao<ContactLocal, ?> contactDAO = db.getContactLocalIntDataDao();
UpdateBuilder<ContactLocal, ?> updateDAO = contactDAO.updateBuilder();
try {
updateDAO.updateColumnValue("isUseless", true);
updateDAO.update();
} catch (SQLException e) {
e.printStackTrace();
}finally {
// db.writeUnlock();
}
ArrayList<ContactLocal> contacts = new ArrayList<>();
while (cur.moveToNext()) {
String id = cur.getString(cur.getColumnIndex(ContactsContract.Contacts._ID));
/** read names **/
String displayName = cur.getString(cur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
/** Phone Numbers **/
Cursor pCur = contentRes.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null, ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?", new String[] { id }, null);
while (pCur.moveToNext()) {
String number = pCur.getString(pCur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
String formatedNo = number.replaceAll("\\s+", "").replace("+", "00").replace("-", "").trim();
try {
QueryBuilder<ContactLocal, ?> query = contactDAO.queryBuilder();
query.where().eq("mFormatedNumber", number);
ContactLocal contact = query.queryForFirst();
if (contact == null) {
contact = new ContactLocal();
contact.setFirstName(displayName.trim());
contact.setLastName(displayName.trim());
contact.setContactNumber(formatedNo);
}
contact.setUseless(false);
contacts.add(contact);
}
}
pCur.close();
}
contactDao.callBatchTasks(new Callable<Void>() {
public Void call() throws Exception {
for (ContactLocal contact : contacts) {
contactDAO.createOrUpdate(contact);
}
}
});
}
}
The main optimization is to use callBatchTasks. From the ormlite documentation:
Databases by default commit changes after every SQL operation. This method disables this "auto-commit" behavior so a number of changes can be made faster and then committed all at once.
By creating an ArrayList and keeping track of the changes, you can use callBatchTasks to create/update at the end all in one shot.
Also I noticed that alreadyUpdated was never accessed, so it's safe to remove.
Also Dao has a createOrUpdate method which is the same as the addContact if statement you had before.
I'm getting this error :
E/AndroidRuntime(8223): Caused by: android.database.CursorWindowAllocationException: Cursor window allocation of 2048 kb failed. # Open Cursors=940 (# cursors opened by this proc=940)
I know its probably because I'm using the Cursor in a wrong way or not closing it at the right time. I think it could be because I'm filling the same cursor without closing/emptying it?
public static void NextAlarmTxt(){
int dan = c.get(Calendar.DAY_OF_WEEK);
long trenutnovrijeme = c.getTimeInMillis();
long bazavrijeme;
long pamti = 0;
String danString = dani(dan);
Cursor CursorDan = DatabaseManager.getAllDataDay(danString);
CursorDan.moveToFirst();
if (!CursorDan.isAfterLast())
{
do
{
bazavrijeme = CursorDan.getInt(2);
if (trenutnovrijeme<bazavrijeme)
{
if (pamti==0)
{
pamti = bazavrijeme;
}
if (pamti>0)
{
if (pamti > bazavrijeme)
{
pamti = bazavrijeme;
}
}
}
if (trenutnovrijeme > bazavrijeme)
{
dan = dan+1;
dani(dan);
CursorDan = DatabaseManager.getAllDataDay(danString);
}
}
while (CursorDan.moveToNext());
}
CursorDan.close();
text1.setText(new StringBuilder("Sljedeći : " ).append(pamti).toString());
}
public static String dani(int dan){
String danString = null;
if (dan==1)
{
danString = "Nedjelja";
}
else if (dan==2)
{
danString = "Ponedjeljak";
}
else if (dan==3)
{
danString = "Utorak";
}
else if (dan==4)
{
danString = "Srijeda";
}
else if (dan==5)
{
danString = "Četvrtak";
}
else if (dan==6)
{
danString = "Petak";
}
else if (dan==7)
{
danString = "Subota";
}
return danString;
}
Cursor Opened :
Cursor CursorDan = DatabaseManager.getAllDataDay(danString);
After that you iterate through the cursor,
do {
...
if (trenutnovrijeme > bazavrijeme)
{
dan = dan+1;
dani(dan);
CursorDan = DatabaseManager.getAllDataDay(danString);
}
....
}
while (CursorDan.moveToNext());
Now, with in the iteration loop, you overwrite the existing cursor with a new one, which leaves the existing one open. I am not sure what you are trying to achieve, but you should not be nesting cursor iteration this way and even if you do that, you should do it in a correct sequece.
Close existing cursor
Retrieve new cursor
Move new cursor to the first item by cursor.moveToFirst()