How to handle SQLiteConstraintException inside a content provider - java

I am using a content provider to read/write from a database that allows a user to organize prescriptions. Currently, I am implementing a DialogFragment that allows the user to enter a new medication. The dialog contains two EditTexts, one for strength, and one for medication name.
The table is designed so that (name, strength) is a unique key. Here is my insert method:
public Uri insert(Uri uri, ContentValues values) {
final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
final int match = sUriMatcher.match(uri);
long _id; // Value used for inserts.
Uri returnUri;
switch(match){
case MEDICATION:
_id = db.insert(PMContract.MedicationEntry.TABLE_NAME, null, values);
if(_id > 0)
returnUri = PMContract.MedicationEntry.buildMedicationUri(_id);
else
throw new UnsupportedOperationException("Failed to insert row into: " + uri);
break;
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
As you can guess, if the user inputs a name and strength that already exist, an SQLiteConstraintException occurs. However, all I do here is throw an UnsupportedOperationException, something that I learned from a tutorial on content providers.
What I would like to do, is handle the situation specifically where a duplicate key is entered, so I can relay that information to the user (likely via a Toast).
I have tried adding a try/catch to where I make the insert call:
try{
getActivity().getContentResolver().insert(PMContract.MedicationEntry.CONTENT_URI, values);
} catch(SQLiteConstraintException e){
}
But SQLiteConstraintException is not the one that is caught, but instead an UnsupportedOperationException.
How can I adjust the ContentProvider code so that I know when I have a ConstraintException, and treat everything else as an UnsupportedOperationException?
EDIT
I have tried to wrap a try/catch block around the db.insert call:
case MEDICATION:
try {
_id = db.insert(PMContract.MedicationEntry.TABLE_NAME, null, values);
} catch(SQLiteConstraintException e){
String s = e.getMessage();
} catch(Exception e1){
String s1 = e1.getMessage();
}
if(_id > 0)
returnUri = PMContract.MedicationEntry.buildMedicationUri(_id);
else
throw new UnsupportedOperationException("Failed to insert row into: " + uri);
break;
And I used the debugger to see the exception that was being thrown by db.insert(). However, the debugger didn't hit a breakpoint in either of the catch blocks, even though the error text was printed in the logcat. I'm not sure why an exception is printed to the logcat but the debugger doesn't hit those breakpoints.

Instead of throwing an exception, return null as your Uri - then you can check the result of the insert() call - if you get a null return value, you know it was a constraint issue.

After trying Ian's suggestion and editing my question, I realized I was having the same problem as this question.
Changing the line of code to db.insertOrThrow() caused the ConstraintException to be thrown if it happened, and I was able to catch that in the DialogFragment:
// In the ContentProvider
_id = db.insertOrThrow(PMContract.MedicationEntry.TABLE_NAME, null, values);
// In the Dialog Fragment
try{
getActivity().getContentResolver().insert(PMContract.MedicationEntry.CONTENT_URI, values);
dismiss();
} catch(SQLiteConstraintException e){
Toast.makeText(getActivity(), "A medication with this name and strength already exists.", Toast.LENGTH_SHORT).show();
} catch(UnsupportedOperationException uoe){
Toast.makeText(getActivity(), "Unable to insert new medication.", Toast.LENGTH_SHORT).show();
}

Related

How to integrate search functionality in Android app

I'm trying to create an application to display the call logs based on the call type (incoming calls, outgoing calls, or missing calls). In addition I'm trying to add search and delete functionalities so the user can search for the call (by number) and delete the call. The layout of the app is shown here:
The search functionality works if I hit the 'All' button to display all calls and search by number there, but upon going to other sections to search for a number, such as received or missed, the app crashes.
So far I have managed to run the logcat on Android Studio, and found that the main issue is in my main activity file. I have attached the logcat image here:
There seems to be issues with these pieces of code: my getCalls Method, afterTextChanged Method, and my updateCursor Method.
private Cursor getCalls(int type, String searchString) {
Uri callUri = CallLog.Calls.CONTENT_URI;
ContentResolver contentResolver = getContentResolver();
String[] projection = new String[]{Calls._ID, Calls.NUMBER, Calls.DURATION, Calls.TYPE};
String selection=null;
String[] selectionArgs=null;
if(type != 0){
// filter type calls
selection = Calls.TYPE + "=?";
selectionArgs = new String[]{String.valueOf(type)};
}
if(!TextUtils.isEmpty(searchString)) {
// has some search string
if(TextUtils.isEmpty(selection)) {
// all call types
selection = Calls.NUMBER + " LIKE ?";
selectionArgs = new String[]{"%"+searchString+"%"};
} else {
// some type of call and add search String
selection = selection+" && " + Calls.NUMBER+" LIKE ?";
selectionArgs = new String[]{selectionArgs[0],"'%"+searchString+"%'"};
}
}
String order = Calls.DATE + " DESC ";
//verify permissions to access the user's call log
int permissionCheck = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CALL_LOG);
if (permissionCheck == PackageManager.PERMISSION_GRANTED) {
cursor = contentResolver.query(callUri, // URI content provider
projection,
selection,
selectionArgs,
order);
}
return cursor;
}
#Override
public void afterTextChanged(Editable s) {
updateCursor();
}
//updates the search
void updateCursor() {
if (cursor != null && !cursor.isClosed()) {
cursor.close();
cursor = null;
}
cursor = getCalls(currentCallType, searchET.getText().toString());
adapter.swapCursor(cursor);
}
Upon running the app, I expected to be able to go to different sections (for example, I go to the received call section) and search for a number, however upon going to the section and tapping on the search bar, the app crashes. I do not understand how there could be issues with these methods.
SQLException says the error is near the & symbol, so the error should be in this line:
selection = selection+" && " + Calls.NUMBER+" LIKE ?";
Let's replace it with this one, because 'AND' should be used in SQL instead of double &:
selection = selection+" AND " + Calls.NUMBER+" LIKE ?";

updating a tuple in a sqlite table in android studio

before voting down please read my question , which I have searched a lot but I couldn't find the answer yet, so I would appreciate if you give me hand to overcome the problem.
Actually I need to update a tuple in a table named "Demographics". But it seems my code does not work correctly, and in fact after running the app , I got the result "0" for updating which means nothing get updated.
12-21 12:34:54.190 2351-2367/? D/Update Result:: =0
I guess my problem is due to not pointing to the right row of the table based on Primary key. Actually when a user Register to my app the following things should happen:
1- Create a tuple in "Demographics" table --> username, password and email will be inserted. An auto increment primary key also constructed and inserted.
2- user logins , then he can complete rest of information in "Demographics" table. --> this MODIFICATION is the "update" process which I', asking.
Would you please tell me if the following codes are wrong or have any implicit error?
DemographicsCRUD.java
public long UpdateDemographics(Demographics_to demoId) {
//SQLiteDatabase db = dbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(DataBaseHelper.lastName, demoId.getD_lastName());
values.put(DataBaseHelper.firstName, demoId.getD_firstName());
values.put(DataBaseHelper.dateOfBirth, demoId.getD_dateOfBirth())
long result = database.update(dbHelper.Demographics_Table, values,
WHERE_ID_EQUALS,
new String[]{String.valueOf(demoId.getD_patientID())});
Log.d("Update Result:", "=" + result);
// db.close();
return result;
}
here is where I call the above code:
private void updateDemographicsTable()
{
ep_demoId = new Demographics_to();
String ep_na = ep_name.getText().toString();
String ep_fa = ep_family.getText().toString();
.
.
.
ep_demoId.setD_dateOfBirth(ep_bd);
ep_demoId.setD_firstName(ep_na);
ep_demoId.setD_lastName(ep_fa);
}
#Override
protected Long doInBackground(Void... arg0) {
long result = ep_demoCRUD.UpdateDemographics(ep_demoId);
return result;
}
#Override
protected void onPostExecute(Long result) {
if (activityWeakRef.get() != null
&& !activityWeakRef.get().isFinishing()) {
if (result != -1)
Toast.makeText(activityWeakRef.get(), "Information Updated!",
Toast.LENGTH_LONG).show();
}}
Looks like whatever you are passing in as the patientID does not have a matching record in the database or the dataobject "Demographics_to" has the patient ID set incorrectly.

Why my query result returns me false

I'm new with java and sql query and for the user connexion, I connect to the DB and check if the login exists. Here is what I do :
requete = "SELECT Login,Password,DroitModifAnnuaire,DroitRecepteurDem,DroitResponsableDem,PiloteIso,Administrateur,DroitNews,DroitTenues,DroitEssai,Nom,Prenom FROM Annuaire WHERE Login='"
+ (request.getParameter("login") + "'");
instruction = connexion.createStatement();
jeuResultats = instruction.executeQuery(requete);
try{
jeuResultats.next();
} catch (SQLException e) {
e.printStackTrace();
}
if (jeuResultats.next() == false) {
loadJSP("/index.jsp", request, reponse);
}else {
loadJSP("/views/menu.jsp", request, reponse);
}
The login that I enter is good but it redirect me to index.jspand I have the error : the result set has no current row
I tried to search answer to this error but I didn't found. So why it returns me false ? While when I do System.out.println(jeuResultats.getString(1)); the login is printed.
jeuResultats.next(); moves your result to the next row. You start with 0th row, i.e. when you call .next() it reads the first row, then when you call it again, it tries to read the 2nd row, which does not exist.
Some additional hints, not directly related to the question:
Java Docs are a good place to start Java 8 ResultSet, for e.x., perhaps ResultSet.first() method may be more suited for your use.
Since you are working with resources, take a look at try-with-resources syntax. Official tutorials are a good starting point for that.
Also take a look at prepared statement vs Statement. Again, official guide is a good place to start
Make the below changes in you code. Currently the next() method is shifting result list to fetch the data at 1st index, whereas the data is at the 0th Index:
boolean result = false;
try{
result = jeuResultats.next();
} catch (SQLException e) {
e.printStackTrace();
}
if (!result) {
loadJSP("/index.jsp", request, reponse);
}else {
loadJSP("/views/menu.jsp", request, reponse);
}
Replace your code by below code:
requete = "SELECT Login, Password, DroitModifAnnuaire, DroitRecepteurDem, DroitResponsableDem, PiloteIso, Administrateur, DroitNews, DroitTenues, DroitEssai, Nom, Prenom FROM Annuaire WHERE Login = '"
+ (request.getParameter("login") + "'");
instruction = connexion.createStatement();
jeuResultats = instruction.executeQuery(requete);
try{
if (jeuResultats.next()) {
loadJSP("/index.jsp", request, reponse);
} else {
loadJSP("/views/menu.jsp", request, reponse);
}
} catch (SQLException e) {
e.printStackTrace();
}

Handle sqlite database in searching

in my app searchbar is there, wheren in user types the text. Whenever a text get changes in the filed i will call a query to DB to get the related search items. But sometimes it crashes.
Here is the code i'm doing to call DB
#Override
public boolean onQueryTextChange(String newText) {
// TODO Auto-generated method stub
if(newText.trim().equals(""))
{
return false;
}
//showSearchSuggestions(newText);
mfilterdata = mController.get_controllerObj().getDBManager().getAllSuggestedFilter(newText);
if(mSearchadapter != null)
mSearchadapter.swapCursor(mfilterdata);
return false;
}
Here is how i m querying in DB manager
public Cursor getAllSuggestedFilter(String filterString)
{
String READ_QUERY = "SELECT * FROM " + Tbl_ITEM_TABLE + " where "+
item.TITLE + " Like" + "\"%" + filterString + "%"+"\"";
if(mcursorForFilter != null)
{
mcursorForFilter.close();
mcursorForFilter = null;
}
try
{
mcursorForFilter = getReadableDatabase().rawQuery(READ_QUERY, null);
}
catch(Exception ee)
{
}
return mcursorForFilter;
}
randomly i get exception like
java.lang.IllegalStateException: attempt to re-open an already-closed object: android.database.sqlite.SQLiteQuery (mSql = SELECT * FROM itemtable where title Like"%t%")
That's probably because you are closing the cursor in the wrong place, and trying to use it after that, but you cannot use an already closed cursor.
I would get rid of this part of the code:
if(mcursorForFilter != null) {
mcursorForFilter.close();
mcursorForFilter = null;
}
Instead, close the old cursor after you set the new one. swapCursor() returns the old Cursor, or returns null if there was not a cursor set, also returns null if the if you try to swap the same instance of the previously set cursor. Knowing that, you can try something like this:
Cursor c = mSearchadapter.swapCursor(mfilterdata);
if(c != null)
c.close();
Try that, and let me know if that helped.
Note that when you are using a Loader (LoaderManager.LoaderCallbacks), the framework is going to close the old cursor. This is what the documentation says:
onLoadFinished:
The loader will release the data once it knows the application is no
longer using it. For example, if the data is a cursor from a
CursorLoader, you should not call close() on it yourself. ...

Java JPA/SAX Handler Events/for loop Not Running

I have 7,464 siteID Integers stored in a List with the same name (siteID). I am looping through the List and using each siteID Integer to query an SQL table with JPA to return a SiteTable instance and get its postcode String.
I am then using those postcode Strings to check XML files to retrieve latitude and longitude values against each postcode. Below is the loop;
for (Integer id : siteID){
siteTable = em.find(SiteTable.class, id);
XMLPositionRetriever.runXMLQuery(siteTable.getPostcode());
}
That postcode String is then put into the runXMLQuery(String toFind) method in the class below;
public class XMLPositionRetriever extends DefaultHandler{
String postcodeToFind;
boolean found = false;
public XMLPositionRetriever(){
}
public XMLPositionRetriever(String toFind){
postcodeToFind = toFind;
}
public static void runXMLQuery(String toFind){
try {
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser saxParser = factory.newSAXParser();
XMLPositionRetriever handler = new XMLPositionRetriever(toFind);
saxParser.parse("src\\haldata\\postcodes"+toFind.charAt(0)+".xml", handler);
}
catch(Exception e){
System.out.println(e);
}
}
#Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if (postcodeToFind.equals(attributes.getValue("postcode"))){
System.out.println("The postcode '"+postcodeToFind+"', has a latitude of "+attributes.getValue("latitude")+" and a longitude of "+attributes.getValue("longitude"));
found = true;
}
}
#Override
public void endDocument(){
if(!found){
System.out.println("Not Found");
}
}
}
The event handlers above make sure that something is always outputted even if the postcode is not found in any of the XML files, or even if there is an exception. So, what I would expect is for the code above to println 7464 times, but instead I am getting 50 or so lines outputted. It appears that the loop isn't actually running for each siteID, but the code says it should be. I've narrowed the problem down to the code I've shown above (most likely within the loop itself) but now I can't see anything wrong. Any suggestions?
The XMLs look like this, but with up to 300,000 entry elements;
<?xml version="1.0"?>
<postcodes>
<entry postcode='AB1 0AA' latitude='7.101478' longitude='2.242852' />
</postcodes>
Sorry to dump so much code on you all, but I don't think I could give you a complete picture with less.
The problem occurs (below, commented);
for (Integer id : siteID){
siteTable = em.find(SiteTable.class, id);
XMLPositionRetriever.runXMLQuery(siteTable.getPostcode());// <--- Null point exception on this line.
}
Where em.find() did not find an Entity class, it returned null. In such cases siteTable.getPostcode() was throwing the NullPointerException. So, I added in an if statement to prevent a null reference going through to that statement (as well as adding in some other conditions to tidy up the XML search).
for (Integer id : siteID){
site = em.find(SiteTable.class, id);
if(site != null && site.getPostcode() != null && !site.getPostcode().equals("")){
XMLPositionRetriever.runXMLQuery(site.getPostcode());
}
else{
System.out.println("The site and/or postcode against this Instruction does not exist.");
}
}
Thank you to MarioP, without whom I probably wouldn't have figured it out.

Categories

Resources