Couchbaselite update operation leads to inconsistent behavior - java

I am using Couchbase Lite SDK for android and saving an object instance of MyClass as a document in the database. MyClass has an attribute that stores the date in the java.util.Date. During run time, I fetch all the instances of MyClass saved in the database and store them in the ArrayList<MyClass>. When I insert a new document into the database and read the values from the database to show all the entered instances, the date field saved in the database is retrieved as a Long when I next try to fetch the details from the database. The code I use to load the details from the database is:
Code snippet 1:
for (Field field: fields) {
field.setAccessible(true);
if (properties.containsKey(field.getName())) {
if ("date".equals(field.getName())) {
Log.d("DebugTag", properties.get(field.getName()) + "");
long dateLong = (Long) properties.get(field.getName());
details.setDate(new Date(dateLong));
} else {
field.set(details, properties.get(field.getName()));
}
} else if("_id".equals(field.getName())) {
details.set_id(document.getId());
} else {
final String msg = "Field " + field.getName() + " not present in document ";
Log.e(TAG, msg);
}
}
You can see that I have added an additional check in case the field is date. This works perfectly fine. So, I save a new entry to database and come back to the page where I see all the entries made into the database.
Now, I have implemented a new functionality to update the details of a record in the database. For updating the record I have the following implementation:
Code snippet 2:
public static boolean updateDocument(Database database, String docID, Map<String, Object> map) {
if (null == database || null == map || null == docID) {
return false;
}
boolean success = true;
Document document = database.getDocument(docID);
try {
// Have to put in the last revision id as well to update the document.
// If we do not do this, this will throw exception.
map.put("_rev", document.getProperty("_rev"));
// Putting new properties in the document ...
document.putProperties(map);
} catch (CouchbaseLiteException e) {
Log.e(TAG, "Error putting property", e);
success = false;
}
return success;
}
After doing this when I try to reload the items, it gives me exception while reading the date field in my Code snippet 1 saying that the Date object cannot be typecasted as Long and the application crashes. Now, when I again open the application, it works perfectly fine with all the changes to the edited entry reflecting correctly. Can anyone let me know the reason for this? I suspect, until we close the database connection, the changes are not committed to the actual database location and the date field in the updated entry is kept in the cache as the Date object in my case.
PS: Although, I have found a workaround for this by setting the date as a Long object in the payload (map in function updateDocument() in Code snippet 2), it would still be interesting to understand the problem I faced.

Looking at this further, this could be reasons related to auto-boxing where long to Long conversion of primitive types to the object wrapper class is crashing the application when trying to save.
Have you tried:
long value = Long.parseLong((String)...);
More specifically in Code snippet 1:
long dateLong = Long.parseLong((String)properties.get(field.getName()));

Related

Cannot parse file using JDBC

Im trying to parse a pipe delimited file and insert fields into a table. when i start the application nothing happens in my DB. My DB has 4 columns (account_name, command_name, and system_name, CreateDt). The file i am parsing has the date in the first row then extra data. The rows following i only need the first 3 fields in each the rest is extra data. the last row is the row count. i skipped the inserting date because for now but want to get back to it after at least able to insert the first 3 fields. I have little experience with parsing a file and storing data in a DB and have looked through jdbc examples to get to this point but im struggling and am sure there is a better way.
File Example
20200310|extra|extra|extra||
Mn1223|01192|windows|extra|extra|extra||
Sd1223|02390|linux|extra|extra|extra||
2
table format
account_name command_name system_name createDt
Mn1223 01192 windows 20200310
Sd1223 02390 linux 20200310
Code to parse and insert into DB
public List insertZygateData (List<ZygateEntity> parseData) throws Exception {
String filePath = "C:\\DEV\\Test_file.xlsx";
List<String> lines = Files.readAllLines(Paths.get(filePath));
// remove date and amount
lines.remove(0);
lines.remove(lines.size() - 1);
for (ZygateEntity zygateInfo : parseData){
new MapSqlParameterSource("account_name", zygateInfo.getAccountName())
.addValue("command_name", zygateInfo.getCommandName())
.addValue("system_name", zygateInfo.getSystemName())
.getValues();
}
return lines.stream()
.map(s -> s.split("[|]")).map(val -> new ZygateEntity(val[0],val[1],val[2])).collect(Collectors.toList());
}
public boolean cleantheTable() throws SQLException {
String sql = "INSERT INTO Landing.midrange_xygate_load (account_name,command_name,system_name)"+
"VALUES (:account_name,:command_name,:system_name)";
boolean truncated = false;
Statement stmt = null;
try {
String sqlTruncate = "truncate table Landing.midrange_xygate_load";
jdbcTemplate.execute(sqlTruncate);
truncated = true;
} catch (Exception e) {
e.printStackTrace();
truncated = false;
return truncated;
} finally {
if (stmt != null) {
jdbcTemplate.execute(sql);
stmt.close();
}
}
log.info("Clean the table return value :" + truncated);
return truncated;
}
}
Entity/Model
public ZygateEntity(String accountName, String commandName, String systemName){
this.accountName=accountName;
this.commandName=commandName;
this.systemName=systemName;
}
//getters and setters
#Override
public String toString() {
return "ZygateEntity [accountName=" + accountName + ", commandName=" + commandName + ", systemName=" + systemName + ", createDt=" + createDt +"]";
}
}
Taking a look at what you've provided, it seems you have a jumbled collection of bits of code, and while most of it is there, it's not all there and not quite all in the right order.
To get some kind of clarity, try to break down what it is you're doing into separate steps, and have a method that focuses on each step. In particular, you write
Im trying to parse a pipe delimited file and insert fields into a table
This naturally breaks down into two parts:
parsing the pipe-delimited file, and
inserting fields into a table.
For the first part, you seem to have most of the parts already in your insertZygateData method. In particular, this line reads all the lines of a file into a list:
List<String> lines = Files.readAllLines(Paths.get(filePath));
These lines then remove the first and last lines from the list of lines read:
// remove date and amount
lines.remove(0);
lines.remove(lines.size() - 1);
You then have some code that looks a bit out of place: this seems to be something to do with inserting into the database, but we haven't created our list of ZygateEntity objects as we haven't yet finished reading the file. Let's put this for loop to one side for the moment.
Finally, we take the list of lines we read, split them using pipes, create ZygateEntity objects from the parts and create a List of these objects, which we then return.
return lines.stream()
.map(s -> s.split("[|]")).map(val -> new ZygateEntity(val[0],val[1],val[2])).collect(Collectors.toList());
Putting this lot together, we have a useful method that parses the file, completing the first part of the task:
private List<ZygateEntity> parseZygateData() throws IOException {
String filePath = "C:\\DEV\\Test_file.xlsx";
List<String> lines = Files.readAllLines(Paths.get(filePath));
// remove date and amount
lines.remove(0);
lines.remove(lines.size() - 1);
return lines.stream()
.map(s -> s.split("[|]")).map(val -> new ZygateEntity(val[0],val[1],val[2])).collect(Collectors.toList());
}
(Of course, we could add a parameter for the file path to read, but in the interest of getting something working, it's OK to stick with the current hard-coded file path.)
So, we've got our list of ZygateEntity objects. How do we write a method to insert them into the database?
We can find a couple of the ingredients we need in your code sample. First, we need the SQL statement to insert the data. This is in your cleanThetable method:
String sql = "INSERT INTO Landing.midrange_xygate_load (account_name,command_name,system_name)"+
"VALUES (:account_name,:command_name,:system_name)";
We then have this loop:
for (ZygateEntity zygateInfo : parseData){
new MapSqlParameterSource("account_name", zygateInfo.getAccountName())
.addValue("command_name", zygateInfo.getCommandName())
.addValue("system_name", zygateInfo.getSystemName())
.getValues();
}
This loop creates a MapSqlParameterSource out of each ZygateEntity object, and then converts it to a Map<String, Object> by calling the getValues() method. But then it does nothing with this value. Effectively you're creating these objects and getting rid of them again without doing anything with them. This isn't ideal.
A MapSqlParameterSource is used with a Spring NamedParameterJdbcTemplate. Your code mentions a jdbcTemplate, which appears to be a field within the class that parses data and inserts into the database, but you don't show the full code of this class. I'm going to have to assume it's a NamedParameterJdbcTemplate rather than a 'plain' JdbcTemplate.
A NamedParameterJdbcTemplate contains a method update that takes a SQL string and a SqlParameterSource. We have a SQL string, and we're creating MapSqlParameterSource objects, so we can use these to carry out the insert. There's not a lot of point in creating one of these MapSqlParameterSource objects only to convert it to a map, so let's remove the call to getValues().
So, we now have a method to insert the data into the database:
public void insertZygateData(List<ZygateEntity> parseData) {
String sql = "INSERT INTO Landing.midrange_xygate_load (account_name,command_name,system_name)"+
"VALUES (:account_name,:command_name,:system_name)";
for (ZygateEntity zygateInfo : parseData){
SqlParameterSource source = new MapSqlParameterSource("account_name", zygateInfo.getAccountName())
.addValue("command_name", zygateInfo.getCommandName())
.addValue("system_name", zygateInfo.getSystemName());
jdbcTemplate.update(sql, source);
}
}
Finally, let's take a look at your cleanThetable method. As with the others, let's keep it focused on one task: it looks like at the moment you're trying to delete the data out of the table and then insert it in the same method, but let's have it just focus on deleting the data as we've now got a method to insert the data.
We can't immediately get rid of the String sql = ... line, because the finally block in your code uses it. If stmt is not null, then you attempt to run the INSERT statement and then close stmt.
However, stmt is never assigned any value other than null, so it remains null. stmt != null is therefore always false, so the INSERT statement never runs. Your finally block never does anything, so you would be best off removing it altogether. With your finally block gone, you can also get rid of your local variable stmt and the sql string, leaving us with a method whose focus is to truncate the table:
public boolean cleantheTable() throws SQLException {
boolean truncated = false;
try {
String sqlTruncate = "truncate table Landing.midrange_xygate_load";
jdbcTemplate.execute(sqlTruncate);
truncated = true;
} catch (Exception e) {
e.printStackTrace();
truncated = false;
return truncated;
}
log.info("Clean the table return value :" + truncated);
return truncated;
}
I'll leave it up to you to write the code that calls these methods. I wrote some code for this purpose, and it ran successfully and inserted into a database.
So, in summary, no data was being written to your database because you were never making a call to the database to insert any. In your insertZygateData method you were creating the parameter-source objects but not doing anything useful with them, and in your cleanThetable method, it looked like you were trying to insert data, but your line jdbcTemplate.execute(sql) that attempted to do this never ran. Even if stmt wasn't null, this line wouldn't work as you didn't pass the parameter values in anywhere: you would get an exception from the database as it would be expecting values for the parameters but you never gave it any.
Hopefully my explanation gives you a way of getting your code working and helps you understand why it wasn't.

OptimisticLockException While deleting EBeans in Play 2 Framework

I am trying to delete an object from a list of an other object. But I'm stuck with a strange Exception. I have tryed multiple thing but here is my final test :
public static Result unpair(String tokenString)
{
Token token = Token.getToken(tokenString);
if(token == null) return ok(toJson(Error.AUTH_ERROR));
String epc = form(UnpairRequest.class).bindFromRequest().get().epc;
//Here the line throwing an Exception
CommitItem.find().where()
.eq("commit.user", token.user)
.eq("epc",epc)
.findUnique().delete();
return ok(toJson(Response.OK));
}
It raise this exception :
OptimisticLockException: Data has changed. updated [0] rows sql[delete from commit_item where epc=? and evat_price=? and ivat_price=? and vat=? and product_product_code=? and commit_id=?] bind[null]
It seems that the Lock is not released after the Select statement but I have no Idea why...
I have also try to delete my object this way :
PairingCommit commit = PairingCommit.find().where()
.eq("user", token.user).findUnique();
CommitItem item = CommitItem.find().where()
.eq("commit", commit)
.eq("epc", epc);
commit.items.remove(item);
commit.update();
But it doesn't delete my item from the database.
I don't really understand what is going wrong with my code.

Neo4j ExecutionEngine does not return valid results

Trying to use a similar example from the sample code found here
My sample function is:
void query()
{
String nodeResult = "";
String rows = "";
String resultString;
String columnsString;
System.out.println("In query");
// START SNIPPET: execute
ExecutionEngine engine = new ExecutionEngine( graphDb );
ExecutionResult result;
try ( Transaction ignored = graphDb.beginTx() )
{
result = engine.execute( "start n=node(*) where n.Name =~ '.*79.*' return n, n.Name" );
// END SNIPPET: execute
// START SNIPPET: items
Iterator<Node> n_column = result.columnAs( "n" );
for ( Node node : IteratorUtil.asIterable( n_column ) )
{
// note: we're grabbing the name property from the node,
// not from the n.name in this case.
nodeResult = node + ": " + node.getProperty( "Name" );
System.out.println("In for loop");
System.out.println(nodeResult);
}
// END SNIPPET: items
// START SNIPPET: columns
List<String> columns = result.columns();
// END SNIPPET: columns
// the result is now empty, get a new one
result = engine.execute( "start n=node(*) where n.Name =~ '.*79.*' return n, n.Name" );
// START SNIPPET: rows
for ( Map<String, Object> row : result )
{
for ( Entry<String, Object> column : row.entrySet() )
{
rows += column.getKey() + ": " + column.getValue() + "; ";
System.out.println("nested");
}
rows += "\n";
}
// END SNIPPET: rows
resultString = engine.execute( "start n=node(*) where n.Name =~ '.*79.*' return n.Name" ).dumpToString();
columnsString = columns.toString();
System.out.println(rows);
System.out.println(resultString);
System.out.println(columnsString);
System.out.println("leaving");
}
}
When I run this in the web console I get many results (as there are multiple nodes that have an attribute of Name that contains the pattern 79. Yet running this code returns no results. The debug print statements 'in loop' and 'nested' never print either. Thus this must mean there are not results found in the Iterator, yet that doesn't make sense.
And yes, I already checked and made sure that the graphDb variable is the same as the path for the web console. I have other code earlier that uses the same variable to write to the database.
EDIT - More info
If I place the contents of query in the same function that creates my data, I get the correct results. If I run the query by itself it returns nothing. It's almost as the query works only in the instance where I add the data and not if I come back to the database cold in a separate instance.
EDIT2 -
Here is a snippet of code that shows the bigger context of how it is being called and sharing the same DBHandle
package ContextEngine;
import ContextEngine.NeoHandle;
import java.util.LinkedList;
/*
* Class to handle streaming data from any coded source
*/
public class Streamer {
private NeoHandle myHandle;
private String contextType;
Streamer()
{
}
public void openStream(String contextType)
{
myHandle = new NeoHandle();
myHandle.createDb();
}
public void streamInput(String dataLine)
{
Context context = new Context();
/*
* get database instance
* write to database
* check for errors
* report errors & success
*/
System.out.println(dataLine);
//apply rules to data (make ContextRules do this, send type and string of data)
ContextRules contextRules = new ContextRules();
context = contextRules.processContextRules("Calls", dataLine);
//write data (using linked list from contextRules)
NeoProcessor processor = new NeoProcessor(myHandle);
processor.processContextData(context);
}
public void runQuery()
{
NeoProcessor processor = new NeoProcessor(myHandle);
processor.query();
}
public void closeStream()
{
/*
* close database instance
*/
myHandle.shutDown();
}
}
Now, if I call streamInput AND query in in the same instance (parent calls) the query returns results. If I only call query and do not enter ANY data in that instance (yet web console shows data for same query) I get nothing. Why would I have to create the Nodes and enter them into the database at runtime just to return a valid query. Shouldn't I ALWAYS get the same results with such a query?
You mention that you are using the Neo4j Browser, which comes with Neo4j. However, the example you posted is for Neo4j Embedded, which is the in-process version of Neo4j. Are you sure you are talking to the same database when you try your query in the Browser?
In order to talk to Neo4j Server from Java, I'd recommend looking at the Neo4j JDBC driver, which has good support for connecting to the Neo4j server from Java.
http://www.neo4j.org/develop/tools/jdbc
You can set up a simple connection by adding the Neo4j JDBC jar to your classpath, available here: https://github.com/neo4j-contrib/neo4j-jdbc/releases Then just use Neo4j as any JDBC driver:
Connection conn = DriverManager.getConnection("jdbc:neo4j://localhost:7474/");
ResultSet rs = conn.executeQuery("start n=node({id}) return id(n) as id", map("id", id));
while(rs.next()) {
System.out.println(rs.getLong("id"));
}
Refer to the JDBC documentation for more advanced usage.
To answer your question on why the data is not durably stored, it may be one of many reasons. I would attempt to incrementally scale back the complexity of the code to try and locate the culprit. For instance, until you've found your problem, do these one at a time:
Instead of looping through the result, print it using System.out.println(result.dumpToString());
Instead of the regex query, try just MATCH (n) RETURN n, to return all data in the database
Make sure the data you are seeing in the browser is not "old" data inserted earlier on, but really is an insert from your latest run of the Java program. You can verify this by deleting the data via the browser before running the Java program using MATCH (n) OPTIONAL MATCH (n)-[r]->() DELETE n,r;
Make sure you are actually working against the same database directories. You can verify this by leaving the server running. If you can still start your java program, unless your Java program is using the Neo4j REST Bindings, you are not using the same directory. Two Neo4j databases cannot run against the same database directory simultaneously.

Copy and past URL in JSP is giving white page

I am new to this forum. I have a doubt about JSP/servlet in my application
I have developed an application in which user may search some data based on some criteria and he will get data from database(through Hibernate to servlet and to JSP). When some data is displayed on screen based on search he/she may try to copy the URL and forward to anyone or If he try to open in different browser it is showing an empty page.
eg: if i try to paste the link given bellow it is showing blank page
example link
but i need to display the data how this can be achieved.
Edited: After clicking on job search in menu bar as mentioned in comments the page will redirect to a servlet
if(action.equals("searchjob")){
String requireskills=request.getParameter("txt_requireSkills");
String location=request.getParameter("txt_locationName");
session.setAttribute("location",location);
String minexp1=request.getParameter("dd_minimum");
String maxexp1=request.getParameter("dd_maximum");
jobsearchDAO = new JobSearchDAOImpl();
List<JobPostInfo> data=jobsearchDAO.jobsearchlist(requireskills,location,minexp1,maxexp1);
if(data!=null && data.size()!=0){
//save data
if(!(session.getAttribute("LoginObject")==null)){
JobSeeker jobSeeker=(JobSeeker)session.getAttribute("LoginObject");
JobSearchCriteria jobsearchcriteria= new JobSearchCriteria();
jobsearchDAO=new JobSearchDAOImpl();
jobsearchcriteria.setKeyWords(requireskills);
jobsearchcriteria.setLocation(location);
JobSeeker jobseeker=(JobSeeker)session.getAttribute("jobseeker");
//
// jobsearchcriteria.setJobSeeker(jobseeker.getJobSeekerSn());
jobsearchcriteria.setJscTs(new Date());
int value=jobsearchDAO.savesearch(jobsearchcriteria);
System.out.println("savesearch value------>"+value);
}
session.setAttribute("jobsearchlist", data);
// session.setAttribute("success","Search Criteria is saved to database.");
response.sendRedirect("jobsearchresult.jsp");
}else
{
session.setAttribute("error","No Records found");
response.sendRedirect("jobsearch.jsp");
}
}
This is the code in DAOIMPL
public List<JobPostInfo> jobsearchlist(String requireskills,String location,String minexp1,String maxexp1) throws Exception{
long minexp;
long maxexp;
try{
session =getSession();
//Criteria Query
Criteria query=session.createCriteria(JobPostInfo.class,"jpost");
// if(minexp1.equals("0") && (maxexp1.equals("") || maxexp1==null)){
if((minexp1.equals("-1") || minexp1=="-1") && maxexp1==null){
}
else if(minexp1.equals("0")){
minexp=Long.parseLong(minexp1);
long min=1;
query.add(Restrictions.lt("jpost.experienceMin",min));
}else if(!(minexp1.equals("") || minexp1==null) && maxexp1.equals("-1")) {
minexp=Long.parseLong(minexp1);
query.add(Restrictions.ge("jpost.experienceMin",minexp));
}else if(!(minexp1==null && maxexp1==null)){
minexp=Long.parseLong(minexp1);
maxexp=Long.parseLong(maxexp1);
query.add(Restrictions.and(Restrictions.ge("jpost.experienceMin",minexp),Restrictions.le("jpost.experienceMax",maxexp)));
}
//For Location
if(!(location==null|| location.equals(""))){
query.createAlias("jpost.location","location");
query.add(Restrictions.like("location.locationName",location).ignoreCase());
}
//For Keyword
if(!(requireskills==null || requireskills.equals(""))){
query.add(Restrictions.like("jpost.requiredSkills","%"+requireskills+"%").ignoreCase());
}//requireskills
List<JobPostInfo> list = query.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY).list();
if(list.size()==0){
return null;
}else{
return list;
}
}catch(HibernateException e){
e.printStackTrace();
}finally {
close(session);
}
return null;
}
I solved my problem. It's a very basic mistake and I hope this will help others:
response.sendRedirect("jobsearchresult.jsp") is replaced by request.getRequestDispatcher("studentinformation.jsp").forward(request, response)
or include-method. The second thing is, the session is created and initialized with the servlet. When I copy the link in a different browser, a certain block of the servlet will be executed. Example:
action.equals("searchjob")
So at the time the session is not available yet, I initialize it in every block like separating declaration and initialization.

Java method halts when retrieving java.sql.Date object from mysql database, why?

My java class (servlet) has been running fine, but recently, I noticed a problem.
In my database, I have a Date column called 'Full_Expiration_Date'. If one was entered it will store it like '2010-11-17'. If one wasn't entered, is stores it like '0000-00-00'.
In my class, I call the records from the db, and look at the 'Full_Expiration_Date' field to see if it has a date or not. If it does, I execute a few lines of code that check to see if before today's date, and if the date is NOT today's date - if both those conditions are met, the coupon has expired.
So my code works just fine when there IS an expiration date specified, but fails when '0000-00-00' is in the field.
I'm generating an array of information for each coupon in the db table. Once the checking is complete and the array is filled, its sent as a request attribute.
Here's a snippet of my code for this process -
rs = stmt.executeQuery("select Full_Expiration_Date from coupons");
//will hold value from "Full_Expiration_Date" field in db
java.sql.Date expirationDate = null;
//today's date - used for comparison
java.util.Date today = new java.util.Date();
while(rs.next()) {
//get expiration date from db for this record
expirationDate = rs.getDate(12);
if(expirationDate == null) { //should be if the field is 0000-00-00, right?
//don't do any checking against
//expiration date, this record
//doesn't have one
couponList[counter][11] = "";
} else { //10
if(expirationDate.before(today) & !today.equals(expirationDate)) {
couponList[counter][11] = "expired";
} else { //11
couponList[counter][11] = "";
}//if
}//if
counter++;
}//while
Can someone pinpoint what I'm doing wrong here? I'm certain, after testing, that it has to do with the field being 0000-00-00.
0000-00-00 is an invalid date that can't be parsed by Java. You need to allow nulls in that field so that MySQL will stop inserting 0s and that way your if(expirationDate == null) check will actually work.
This is stupid behavior IMO and you should "fix it" by turning on Strict mode. http://dev.mysql.com/doc/refman/5.1/en/server-sql-mode.html#sqlmode_no_zero_date
You can work around this using MySQL's "zeroDateTimeBehavior" configuration property. In your connection properties set:
"zeroDateTimeBehavior" to "convertToNull"
Anytime a 0000-00-00 date is retrieved, it will return null instead of throwing an exception.
Reference: http://dev.mysql.com/doc/refman/5.0/en/connector-j-reference-configuration-properties.html

Categories

Resources