How should you handle MongoDB-Exceptions in Java? - java

I want to handle exceptions, which are thrown from a query (find(...).first()) to MongoDB (Driver 3.7) in Java (the database is not stored locally). However there are no possible exceptions named in the JavaDocs and also in the MongoDB documentaton itself. Can there really occur no exceptions? I doubt that, because I think there could occur e.g. some network errors.
My queries look something like this:
final MongoCollection<Document> collection = database.getCollection("my-collection");
final Bson bsonFilter = Filters.eq("someName", "test");
final Document result = collection.find(bsonFilter).first();

Consider the following code. It connects to a MongoDB instance locally and gets a collection named "test" from the database named "users".
final String connectionStr = "mongodb://localhost/";
MongoClient mongoClient = MongoClients.create("mongodb://localhost/");
MongoDatabase database = mongoClient.getDatabase("users");
MongoCollection<Document> collection = database.getCollection("test");
If you provide a wrong host name for the connectionStr value, like "mongodb://localhostXYZ/" (and no such host exists) the code will throw an exception, like:
com.mongodb.MongoSocketException: localhostXYZ},
caused by {java.net.UnknownHostException: localhostXYZ}}],
..., ...
com.mongodb.MongoSocketException is a MongoDB Java driver exception. It is a runtime exception. It is also a sub-class of MongoException. From the MongoDB Java API:
public class MongoException extends RuntimeException
Top level Exception for all Exceptions, server-side or client-side, that come
from the driver.
The documentation also lists the following are sub-classes (all are runtime exceptions)
MongoChangeStreamException, MongoClientException, MongoExecutionTimeoutException, MongoGridFSException, MongoIncompatibleDriverException, MongoInternalException, MongoInterruptedException, MongoServerException, MongoSocketException.
So, all the exceptions thrown by MongoDB Java driver APIs are runtime exceptions. These are, in general, not meant to be caught and handled (but, you know how to use try-catch, and a runtime exception can be caught and handled).
Let us consider your code:
final MongoCollection<Document> collection = database.getCollection("my-collection");
final Bson bsonFilter = Filters.eq("someName", "test");
final Document result = collection.find(bsonFilter).first();
The first statement database.getCollection("my-collection"), when it runs the code is looking for a collection named "my-collection".
If you want to make sure the collection exists in the database, then verify using the listCollectionNames​() and check the collection name exists in the returned list. In case the collection name doesn't exist, you can throw an exception (if you want to). This exception is what you have figure:
if you want to tell the user or the application that there was no
such collection named "my-collection", you can show or print a
message saying so (and then abort the program) or throw a runtime
exception with an appropriate message.
So, the code might look like this:
if listCollectionNames​() doesn't contain "my-collection"
then
print something and abort the program
-or-
throw a runtime exception
else
continue with program execution
Your code final Document result = collection.find(bsonFilter).first(); is not correct. collection.find returns a FindIterable<TDocument> not a Document. So, the query output can be determined by further examining the FindIterable object; it may have documents or none. And, the find method doesn't throw any exceptions.
Based on if there are any documents returned or not you can show a message to the client. This is not a case you throw an exception.

Related

Jooq not throwing exception for failures

I am using Jooq to populate CSV data into my DB.
If I provide "String Value" instead of Int It is not entering the value into DB but in the mean time it is not throwing the error also.
How do I know if the upload is failed or not.How to handle these type of exceptions.In addition that is there any way to check/throw warning if i try to give string in int column.
version : 3.8.x
Connection connection = getConnection()
try(Connection connection = getConnection()) {
           DSLContext create = DSL.using(connection, SQLDialect.MYSQL);
           create.loadInto(Tables.PROCESS_QUEUE_MAP)
                   .loadCSV(new File("/my/folder/testInput.csv"))
                   .fields(Tables.PROCESS_QUEUE_MAP.PROCESS_QUEUE_ID,
                           Tables.PROCESS_QUEUE_MAP.PROCESS_NAME,
                           Tables.PROCESS_QUEUE_MAP.QUEUE_NAME,
                           Tables.PROCESS_QUEUE_MAP.MARKEPTLACE,
                           Tables.PROCESS_QUEUE_MAP.QUEUE_TYPE,
                           Tables.PROCESS_QUEUE_MAP.CREATED_BY,
                           Tables.PROCESS_QUEUE_MAP.CREATED_TIME,
                           Tables.PROCESS_QUEUE_MAP.LAST_MODIFIED_BY,
                           Tables.PROCESS_QUEUE_MAP.LAST_MODIFIED_TIME)
                   .execute();
       } catch(Exception ex) {
           ex.printStackTrace();
       }
   }
General failure handling with the Loader API
The loader API by default throws all kinds of exceptions that are raised from the underlying database or JDBC driver. This can be configured and overridden by specifying:
LoaderOptionsStep.onErrorAbort() (default)
LoaderOptionsStep.onErrorIgnore()
This only affects JDBC errors, not data loading "errors"
jOOQ's auto-conversion
For historic reason, throughout the jOOQ API, the automatic conversion between data types is "lenient" instead of "fail-fast". All data type conversion passes through the Convert utility, which returns null in case a data type conversion fails. E.g. when calling Convert.convert(Object, Class), the following test will pass:
assertNull(Convert.convert("abc", int.class));
This has been criticised in the past, but cannot be changed easily in the jOOQ API due to backwards compatibility.
Workarounds include:
Parsing the CSV content yourself
Passing an Object[][] to LoaderSourceStep.loadArrays()

Is catching MongoException necessary?

Suppose i have the following code:
...
this.mongoTemplate.getCollection(sessionId).remove(new BasicDBObject();
...
Hovering over remove, displays
Remove documents from a collection.
Parameters: query the deletion criteria using query operators. Omit
the query parameter or pass an empty document to delete all documents
in the collection. Returns: the result of the operation Throws:
com.mongodb.WriteConcernException - if the write failed due some other
failure specific to the delete command MongoException - if the
operation failed for some other reason #mongodb.driver.manual
tutorial/remove-documents/ Remove Documents
Compiler is not complaining about possible to be thrown exception, so i'm wondering if there's need to catch MongoException in these and many other calls of in mongodb java driver

Consistency level ALL used while statement has consistency level TWO defined

We use java datastax cassandra driver 2.1.2. Cassandra version we use is 2.0.9.
We have statement which we build with QueryBuilder and we are setting consistency level to statement on TWO explicitly.
Select selectStatement = QueryBuilder.select().from(ARTICLES);
selectStatement.where(eq(ORGANIZATION_ID, organizationId));
selectStatement.setConsistencyLevel(ConsistencyLevel.TWO);
final ResultSet rs = session.execute(selectStatement);
//call to all() will be removed since it is enough to iterate over result set
//and then you get pagination for free instead of loading everything in
//memory
List<Row> rows = rs.all();
for (final Row row : rows) {
//do something with Row, convert to POJO
}
We get exception like this:
com.datastax.driver.core.exceptions.ReadTimeoutException: Cassandra timeout during read query at consistency ALL (3 responses were required but only 2 replica responded)
com.datastax.driver.core.exceptions.ReadTimeoutException.copy (ReadTimeoutException.java:69)
com.datastax.driver.core.DefaultResultSetFuture.extractCauseFromExecutionException (DefaultResultSetFuture.java:259)
com.datastax.driver.core.ArrayBackedResultSet$MultiPage.prepareNextRow (ArrayBackedResultSet.java:279)
com.datastax.driver.core.ArrayBackedResultSet$MultiPage.isExhausted (ArrayBackedResultSet.java:239)
com.datastax.driver.core.ArrayBackedResultSet$1.hasNext (ArrayBackedResultSet.java:122)
com.datastax.driver.core.ArrayBackedResultSet.all (ArrayBackedResultSet.java:111)
I know that calling all() on ResultSet makes it load all articles for organization in memory and work with it and creates load on cassandra. This will be removed as noted in comments. This can cause read timeout but I am still puzzled why in exception message there is ALL.
Question is why exception is telling that consistency level ALL is used when we set it to TWO for original statement. Is all() internally doing something with query and using CL ALL by default?
Your problem is almost certainly https://issues.apache.org/jira/browse/CASSANDRA-7947 . You are seeing an error message from failing to perform read repair. It is unrelated to your original consistency level. This is fixed in 2.1.3+.

MongoDB handling failure of insert in Java

I would like to handle failure of insert to collection (using Java) in order to be sure that my insert was successful. In case that insert fails, I want to perform some fall-back action.
Assuming following code in Java and latest mongo driver (version 2.11.3):
BasicDBObject doc = new BasicDBObject("name", "MongoDB");
WriteResult result = coll.insert(WriteConcern.SAFE, doc);
I am confused by the fact that the insert returns WriteResult and it could throw MongoException. What am I supposed to do in order to safely detect any possible failure of insert? Please, provide code example. And if you can clarify when insert throws exception and when it just returns some error write result. I tried to search in java driver API docs at http://api.mongodb.org/java/2.11.3/ for it; howerever, this infromation is missing there.
WriteResult result;
try {
result = coll.insert(WriteConcern.SAFE, doc);
}
catch(MongoException ex){
logger.warn("Insert failed.", ex);
throw ex;
}
//Shall I check result here for additional errors?
If I should check, what type of error I am able to detect by checking insert result?
You need to take a look at "WriteConcern", it has the all behaviors you need.
You can use it per one write like this:
coll.insert(dbObj, WriteConcern.SAFE);
If you use WriteConcern.SAFE your operation will wait for an acknowledgement from the primary server, so if no exception is raised then you're ok.
Or you can set default behaviour for all write operations when you are creating MongoClient:
MongoClientOptions.Builder builder = new MongoClientOptions.Builder();
builder.writeConcern(WriteConcern.JOURNAL_SAFE);
MongoClient mongoClient = new MongoClient(
new ServerAddress("localhost"), builder.build());
[Based on Colin Morelli's comment] If you don't use a WriteConcern that raises exceptions, you can use the WriteResult.getLastError() to determine if it was successful or not. Similarly, if you use WriteConcern.SAFE, and the write succeeds, WriteResult will have useful information on it such as the number of records that were written.
Here you can read about WriteConcern in general.

how to fetch only reason from java exception

when I connect to database and execute a query, then if there occurs any exception then I want to fetch only the reason of exception not the full message , so that in my log I can log only the reason of exception for example. one exception is below by applying getMessage() on exception object in catch block
[Microsoft][ODBC SQL Server Driver][SQL Server]Invalid object name 'tty'.
So I want to fetch only "Invalid object name 'tty'".
The exception will occur only due to executeUpdate() method. so exception may be anything related to database or sqlquery not just Invalid object name 'tty'.
I can't see anything obvious in SQLException, but I can think of two options:
Remove everything in square brackets, possibly with a regex.
If you're always using the same driver, just remove the leading predictable leading substring
Sample code for the second option:
private static final String MESSAGE_PREFIX =
"[Microsoft][ODBC SQL Server Driver][SQL Server]";
...
String message = exception.getMessage();
if (message.startsWith(MESSAGE_PREFIX))
{
message = message.substring(MESSAGE_PREFIX.length());
}
That's likely to be considerably easier than the first option, but is obviously rather more brittle in the face of driver changes. I wouldn't be surprised to see the message format change for different drivers anyway though...
as #Jon Skeet suggested I think better to use first option for regex and apply following code in catch block
Pattern p=Pattern.compile("\[.*\]");
Matcher m=p.matcher(e.getMessage());
int end=0;
while(m.find()){
end=m.end();
System.out.println(m.start()+" "+m.end());
}
System.out.println(e.getMessage());
System.out.println(e.getMessage().substring(end));
then it gives following output
0 47
[Microsoft][ODBC SQL Server Driver][SQL Server]Invalid object name 'tty'.
Invalid object name 'tty'.

Categories