Codename One SQL database storing wrong values - java

I am used to developing desktop applications with Java. Now I am trying Codename One to develop my first mobile app.
Trying to replicate my experiences with SQL databases I am running into a very odd storage behavior, which I cannot explain.
The database is created, but when I change the table input value, the new value gets ignored and just the old value is added. To save the new value, I have to delete the database.
I like the interface and any kind help would be appreciated.
Database db = Display.getInstance().openOrCreate("MyDB.db");
db.execute("CREATE TABLE IF NOT EXISTS Persons (Date NOT NULL,Event NOT NULL)");
String sql = "INSERT INTO Persons (DATE , Event) " + "VALUES ( 'John', '10000.00' );";
db.execute (sql);
// adds "John" to the database every time I click the button
// then I change the from "John" to "James"
// I am not adding the lines twice, just change the input
String sql = "INSERT INTO Persons (DATE , Event) " + "VALUES ( 'James', '10000.00' );";
db.execute (sql);
//keeps adding "John" to the database, even though value has been changed to "James"
Cursor cur = db.executeQuery("select * from Persons;");
Row currentRow= cur.getRow();
String dataText = currentRow.getString(0);
while (cur.next()) {
System.out.println(dataText);
}

You're not fetching the next row into dataText in your while() loop, so you're just repeatedly printing out the text from the first row.
It should be:
Cursor cur = db.executeQuery("select * from Persons;");
while (cur.next()) {
Row currentRow = cur.getRow();
String dataText = currentRow.getString("Date");
System.out.println(dataText);
}
If you examine the table with a separate query tool, like PhpMyAdmin, you should see that it contains both rows.
I hope I got the syntax right. I'm not a Java programmer and I got it from a tutorial.

Related

Discord Java JDA | deleting data from SQLite / getting data from SQLite [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
I'm trying to make a ticket-based support system and I would like to know how to read and delete data from a SQLite table.
The system will work like this:
You click on a reaction and the bot checks if you already have a dedicated channel, if not it will create one.
If you close the ticket by clicking on a reaction in your personal channel, the channel and your data will be deleted.
That's my code so far:
public void onMessageReactionAdd(MessageReactionAddEvent event) {
if(!event.getUser().isBot()) {
if(event.getChannel().getIdLong() == 747412032281772033l && event.getReactionEmote().getEmoji().equals("\uD83C\uDFAB")) {
ResultSet set = LiteSQL.onQuery("SELECT channelid FROM ticketchans WHERE guildid = " + event.getGuild().getIdLong() + " AND userid = " + event.getUserIdLong());
try {
Long user = set.getLong("userid");
if(!(user == event.getUserIdLong())){
Category cat = ((GuildChannel) event.getChannel()).getParent();
TextChannel chan = cat.createTextChannel(event.getMember().getEffectiveName() + "'s TicketChannel").complete();
EmbedBuilder builder = new EmbedBuilder();
builder.setDescription("Hi " + event.getMember().getAsMention() + ", bitte beschreibe hier detailiert dein Anliegen. Wenn du dein ticket schliessen willst klicke auf das X");
builder.setColor(Color.decode("#910cc9"));
chan.sendMessage(builder.build()).queue(Message -> {
Message.addReaction("\u274C").queue();
});
set.next();
LiteSQL.onUpdate("INSERT INTO ticketchans(guildid, channelid, userid) VALUES(" +
event.getGuild().getIdLong() + ", " + event.getChannel().getIdLong() + ", " + event.getUserIdLong() + ")");
event.getChannel().sendMessage(event.getUser().getAsMention() + " TicketChannel eröffnet!").complete().delete().queueAfter(4, TimeUnit.SECONDS);
}
}catch (SQLException e) {}
}
if(event.getReactionEmote().getEmoji().equals("\u274C")) {
//delete data in table event.getGuild().getGuildChannelById(event.getChannel().getIdLong()).delete().reason("").queue();
}
}
}
Getting Data from SQLite
Most of this applies to SQL in general and isn't specific to SQLite.
First off, a SELECT statement consists of different parts.
SELECT columns FROM table WHERE condition;
For columns you have to fill in the names of the columns you want to get from your table. Pretty self-explanatory.
If you want to select more than one column, you just have to list them with commas, like this:
SELECT column1, column2, column3 FROM table WHERE condition;
In order to select every column of your table you just write * instead of the columns.
SELECT * FROM table WHERE condition;
Note: You can only access columns in your ResultSet if you selected them in your statement. If you select channelid you won't be able to get userid, unless you select it as well. (SELECT channelid, userid FROM table WHERE condition;)
You seem to understand the WHERE part so I will skip it. In case you need some more help or want to expand your usage of SQLite even more, you may check out some tutorials online.
Now, after writing your correct SELECT statement it's time to access the data in Java.
Therefore, you have to loop through your ResultSet.
ResultSet rs = LiteSQL.onQuery(
"SELECT channelid, userid
FROM ticketchans
WHERE guildid = " + event.getGuild().getIdLong() + "
AND userid = " + event.getUserIdLong()
);
// loop through the result set
while (rs.next()) {
Long userid = rs.getLong("userid");
Long channelid = rs.getLong("channelid");
}
You now have the data you need and can use it for whatever you want.
Deleting Data from SQLite
Most of this applies to SQL in general and isn't specific to SQLite.
The DELETE statement has a similar structure to the SELECT statement although it lacks the columns (of course).
DELETE FROM table WHERE condition;
As explained in the first part, you have to choose the table you want to delete data from and then narrow it down using conditions.
In your case, deleting a specific ticket would be like this:
DELETE FROM ticketchans WHERE guildid = GID and userid = UID and channelid = CID;
If you don't use all three IDs in the condition, you might end up deleting all tickets of a guild or of an user. Since the channelid is always unique you could possibly skip the userid = UID part, but the details are up to you.
As already mentioned, if you want more specific statements or need some variations, check out a tutorial of your liking. (The one provided is just an example, use whatever you are comfortable with.)
On another note: I would advice not using .complete() but .queue() instead.
If you want to know why and how, check out this page.

How count the frequency of occurence of each value in a column in Table "A" and insert in a Table "B" using SQLitein an android app

I have a simple app that contains an Sqlite database containing 2 tables:
TABLE_CHAP that contains:
_id
Chapter_title
Number_of_flashcards
TABLE_Flash contains:
_id
Chap_id
flashcard content
I upload the content of the database to the assets folder, TABLE_FLASH contains a number of flashcards that belongs to each chapter.
What I'm trying to do is to count the frequency of Chap_id in TABLE_FLASH and insert this number to Number_of_flashcards in TABLE_CHAP and afterward display the number_of_flashcard in front of the concerned Chapter_Title.
the Number_of_flashcards is dynamic as the user may add his own flashcards to each chapter.
public void nberOfFlahcards(){
int xy= getChap_tableCount();
ContentValues contentValues = new ContentValues();
database = openHelper.getWritableDatabase();
for(int i=1; i<=xy; i++){
String countQuery = "SELECT * FROM TABLE_CHAP WHERE Chap_ID = " + i;
Cursor cursor = database.rawQuery(countQuery, null);
int total_count=cursor.getCount();
Log.v(TAG, "Nber of Flashcards for chapter"+i+"is: "+total_count);
contentValues.put(KEY_NBER_FLAHCARDS, Integer.toString(total_count));
database.update(TABLE_CHAP,contentValues, KEY_NBER_FLAHCARDS,null );
this code gave me always 0, please check where's the error and if you have better code or better architecture for the database please advise.
This update can be done with a single query
UPDATE TABLE_CHAP SET Number_of_flashcards =
(SELECT count(*)
FROM TABLE_Flash AS tf
WHERE tf.Chap_id=TABLE_CHAP._id)
But I'd better have a method int getNumberOfFlashcards(int chap_id) which computes the count for the given chap_id.

Android - Fastest way to search data in SQLite database

I have an image processing app. My app stores the already processed images in a database. Every time the user opens the app, the app starts to check the database to see what photos have already been processed. With my code this process is taking around 10-20 seconds, which for my needs is a lot of time.
The database only has one column, the path of the image. I take the full image list from the phone and then search every item of the list in the database.
My code is as follows:
public static ArrayList<String> getAlreadyProcessedPhotos(Context context, ArrayList<String> photos, SQLiteDatabase db)
{
ArrayList<String> notAlreadyProcessedPhotos = new ArrayList<>();
for(String path : photos)
{
File imgFile = new File(path);
if (!Utils.isAlreadyProcessed(context, imgFile, db))
{
notAlreadyProcessedPhotos.add(path);
}
}
return notAlreadyProcessedPhotos;
}
public static boolean isAlreadyProcessed(Context context, File imgFile, SQLiteDatabase photosDb) {
if(photosDb == null || !photosDb.isOpen())
photosDb = new DatabaseHelper(context).getReadableDatabase();
String searchQuery = "SELECT * FROM " + DatabaseHelper.TABLE_NAME + " WHERE " + DatabaseHelper.PATH_COLUMN + "=?";
Cursor cursor = photosDb.rawQuery(searchQuery, new String[] {imgFile.getAbsolutePath()});
boolean result = cursor.moveToFirst();
cursor.close();
return result;
}
For each file that you want to check you are executing a separate sqlite query. No wonder it's slow! If there are 100 files you will need to do a 100 queries. But this can really be done with one simple query. You just need to combine your two methods into 1
public static ArrayList<String> getAlreadyProcessedPhotos(Context context, ArrayList<String> photos, SQLiteDatabase db)
{
ArrayList<String> notAlreadyProcessedPhotos = new ArrayList<>();
ArrayList<String> preProc = new ArrayList()
for (String item: photos) {
preProc.add("'" + item + "'");
}
String inClause = TextUtils.join(",", preProc);
String searchQuery = "SELECT " + DatabaseHelper.PATH_COLUMN + "FROM " + DatabaseHelper.TABLE_NAME + " WHERE " + DatabaseHelper.PATH_COLUMN + "NOT IN (" +inClause + ")";
Cursor cursor = photosDb.rawQuery(searchQuery);
while(cursor.moveToNext())
{
notAlreadyProcessedPhotos.add(cursor.getString(0);
}
return notAlreadyProcessedPhotos;
}
This is one loop, one query. I don't know where your photos array list comes from but I get the feeling there is room for further optimization there as well.
The answer to almost all sql (Sqlite, MySql, ....) speed issues is to create an index on the table. See: https://www.sqlite.org/lang_createindex.html
My guess your doing a full table scan on the imgFile you just added, that is as slow as it gets.
Other things you can do ( But won't help near as much as an index)
1) Since you are not using the imgFile returned from Sqlite, change your sql to 'Select count() From ... ' which will return an integer that is greater than zero if present.
2) Add a limit clause to the select statement "Select .... limit 1;" This will allow Sqlite to return once the first record is found.
You already got the responses in form of the comments too!
First is the loop issue as how suggested e4c5. Of course that will make a huge boost.
The second is the SELECT * FROM table replace with SELECT field1WhatIreallyNeed, field2WhatIreallyNeed FROM table.
It helps adding to index the Where fields too.
I have integrated sqlite3 with NDK , so it is used C there and is even faster, but worth if your records are close to 1 million in 1 table.
The best answer is in comment: you don't need database for this! And that would be the fastest. Think about the database how is read, where is stored? - in a file not, just with another constraints, parsing, processing overheads.
I need the database, becuase my app overwrites the original photo, so
the photo always exists
No, you don't need database for this!
There are eTags
There is a meta file info
You can store in an separate file downloaded_timestamp, processed_timestamp and you can calculate if needs to be processed or not, and that will take milliseconds and not 10-20 seconds.
So, drop your database and use a simple file, read the data from that file all at once, not line by line.

Java adding data to table using SQL

I am using an sql query to add data data to an existing database table.
I want to add data under the columns 'Room_Resource' and 'Quantity'.
The system is designed to allow bookings and i am trying to add bookings made to a tblBookings table, the code below is taken from JButton clicked function.
The value I want to add to Room_Resource is a name taken from a selected table within the system. I declared a variable for this 'resourceChosenString'
The value I want to add to quantity is from the 'Quantity' variable i have declared in relation to a combo box.
Here are my declarations:
int selectedResourceRow = tblResources.getSelectedRow();
Object resourceChosen = tblResources.getValueAt(selectedResourceRow,1);
String resourceChosenString = resourceChosen.toString();
int Quantity = cmbQuantity.getSelectedIndex();
I then have a sql statement:
String sql = ("INSERT INTO tblBookings (Room_Resource,Quantity) VALUES (" + resourceChosenString + " ', ' " + Quantity + " ',) ");
And then the execute code:
try{
pst = conn.prepareStatement(sql);
pst.execute();
JOptionPane.showMessageDialog(null, "Added");
} catch (Exception e){
JOptionPane.showMessageDialog(null, "Error Adding Booking");
}
Currently it gives me an error when I attempt to add the data to the table and wondered if anyone had any suggestions?
Also I considered that perhaps the problem could lie in the fact I have more than two columns in the external table and the table I am adding the data to so columns could be left blank. If this could be the problem, could anyone tell me how to get around it? Possibly if there is a null function I can use instead of values.
You probably want to tell us what database you're using and what error message you're getting. But just off the bat, it looks like your sql string is not formatted correctly. I don't know if you mistyped it in the question or if your code has a simple syntax error.
Just shooting from the hip with what you have, it looks like your sql statement should be:
String sql = "INSERT INTO tblBookings (Room_Resource,Quantity) VALUES ('" + resourceChosenString + "', " + Quantity + ")";
Notice that resourceChosenString should be wrapped in single quotes (you're missing the single quote on the left). Also, I don't think you're supposed to wrap a number in single quotes (I could be wrong since I don't know which database you're using).
Qwerky is right though; you should use a PreparedStatement.
The SQL you are generating is not valid and looks like this;
INSERT INTO tblBookings (Room_Resource,Quantity) VALUES (resource ', ' 1 ',)
^ ^
missing quote extraneous comma
You should tidy it up, or better still use a PreparedStatement.
String sql = "insert into tblBookings (Room_Resource,Quantity) values (?, ?)";
PreparedStatement pst = conn.prepareStatement(sql);
pst.setString(1, resourceChosenString);
pst.setInt(2, quantity); //variable names are not capitalised by convention
pst.execute();

Failing to load large dataset into h2 database

Here is the problem: At my company we have a large database that we want to perform some automated operations in it. To test that we got a small sample of that data about 6 10MB sized csv files. We want to use H2 to test the results of our program in it. H2 Seemed to work fine with our previous cvs though they were, at most, 1000 entries long. When it comes to any of our 10MB files the command
insert into myschema.mytable (select * from csvread('mycsvfile.csv'));
reports a failure because one of the registries is supposedly duplicated and offends our primary key constraints.
Unique index or primary key violation: "PRIMARY_KEY_6 ON MYSCHEMA.MYTABLE(DATETIME, LARGENUMBER, KIND)"; SQL statement:
insert into myschema.mytable (select * from csvread('src/test/resources/h2/data/mycsvfile.csv')) [23001-148] 23001/23001
Breaking the mycsvfile.csv into smaller pieces I was able to see that the problem starts to appear after about 10000 rows inserted(though the number varies depending on what data I used). I could however insert more than 10000 rows if I broke the file into pieces and then ran the command individually. But even if I manage to insert all that data manually I need an automated method to fill the database.
Since running the command would not give me the row that was causing the problem I guessed that the problem could be some cache in the csvread routine.
Then I created a small java program that could insert the data in the H2 database manually. No matter whether I batched the commands, closed and opened the connection for 1000 rows h2 reported that I was trying to duplicate an entry in the database.
org.h2.jdbc.JdbcSQLException: Unique index or primary key violation: "PRIMARY_KEY_6 ON MYSCHEMA.MYTABLE(DATETIME, LARGENUMBER, KIND)"; SQL statement:
INSERT INTO myschema.mytable VALUES ( '1997-10-06 01:00:00.0',25485116,1.600,0,18 ) [23001-148]
Doing a normal search for that registry using emacs I can find that the registry is not duplicated as the datetime column is unique in the whole dataset.
I cannot give that data for you to test since the company sells that information. But here is how my table definition is like.
create table myschema.mytable (
datetime timestamp,
largenumber numeric(8,0) references myschema.largenumber(largecode),
value numeric(8,3) not null,
flag numeric(1,0) references myschema.flag(flagcode),
kind smallint references myschema.kind(kindcode),
primary key (datetime, largenumber, kind)
);
This is how our csv looks like:
datetime,largenumber,value,flag,kind
1997-06-11 16:45:00.0,25485116,0.710,0,18
1997-06-11 17:00:00.0,25485116,0.000,0,18
1997-06-11 17:15:00.0,25485116,0.000,0,18
1997-06-11 17:30:00.0,25485116,0.000,0,18
And the java code that would fill our test database(forgive my ugly code, I got desperate :)
private static void insertFile(MyFile file) throws SQLException {
int updateCount = 0;
ResultSet rs = Csv.getInstance().read(file.toString(), null, null);
ResultSetMetaData meta = rs.getMetaData();
Connection conn = DriverManager.getConnection(
"jdbc:h2:tcp://localhost/mytestdatabase", "sa", "pass");
rs.next();
while (rs.next()) {
Statement stmt = conn.createStatement();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < meta.getColumnCount(); i++) {
if (i == 0)
sb.append("'" + rs.getString(i + 1) + "'");
else
sb.append(rs.getString(i + 1));
sb.append(',');
}
updateCount++;
if (sb.length() > 0)
sb.deleteCharAt(sb.length() - 1);
stmt.execute(String.format(
"INSERT INTO myschema.mydatabase VALUES ( %s ) ",
sb.toString()));
if (updateCount == 1000) {
conn.close();
conn = DriverManager.getConnection(
"jdbc:h2:tcp://localhost/mytestdatabase", "sa", "pass");
updateCount = 0;
}
}
if (!conn.isClosed()) {
conn.close();
}
rs.close();
}
I'll be glad to provide more information if requested.
EDIT
#Randy I always check if the database is clean before running the command and in my java program I have a routine to delete all data from a file that fails to be inserted.
select * from myschema.mytable where largenumber = 25485116;
DATETIME LARGENUMBER VALUE FLAG KIND
(no rows, 8 ms)
The only thing that I can think of is that there is a trigger on the table that sets the timestamp to "now". Although that would not explain why you are successful with a few rows, it would explain why the primary key is being violated.

Categories