I have developed a mobile application using the CodeName One plugin for Java in the Netbeans IDE.
CodeName One uses the Database API. https://www.codenameone.com/javadoc/com/codename1/db/Database.html
I'm running some tests (there are around 10 values I would like to upload, however, just testing the connection ect by uploading ID, Fname and Lname values.
Database db = null;
Cursor cur = null;
String Fname = findTxtFirstn(c).getText();
String Lname = findTxtLastn(c).getText();
try{
Database ARdb = Display.getInstance().openOrCreate("RecordsDB.db");
System.out.println("Connection secured to database.");
ARdb.beginTransaction();
String createTable = "CREATE TABLE IF NOT EXISTS RecordsTable ("
+ "ID integer PRIMARY KEY,"
+ "First_Name text NOT NULL,"
+ "Last_Name text NOT NULL)";
String query = "insert into RecordsTable (ID,First_Name,Last_Name) values (3,'Test','Testerton')";
ARdb.execute(createTable);
ARdb.execute(query);
ARdb.commitTransaction();
} catch(Exception e){
System.out.println("Error! Connection Failed to DB" +e.getMessage());
} finally {
Util.cleanup (db);
Util.cleanup(cur);
}
I get no errors and everything runs, however, the values are not in the database when I check it. Am I missing something here? I have followed tutorials and looked over the Codename One API. I can't find the solution.
Edit: I need to change the value of the primary number each run (else I get an error: number needs to be unique), This tells me the values are being stored on the database, unfortunately, when I check the database in question there are no records on it, so where it the data going?
I used DB Browser for SQLite.
Have you tried performing your modifying change in a transaction? In databases with transactions all modifying operations (CREATE, INSERT, ...) need to be performed in a transaction.
ARdb.beginTransaction();
// your create code
ARdb.commitTransaction();
Related
I want to create testdata and have written a function for storing the products, my product generators generate in my database.
The plan is to create about 10,000,000 products or more for testing purposes.
I want to check every time before I insert a product, if the same product name exists.
If it does, the product isn't stored in the database.
I know that the performance issue is the checking if the products exist, which takes longer and longer the more products are in the database. But there is no other way, I know, how I can improve this issue.
I may use indexes, but I don't know how to in this scenario.
If you have other ideas how to improve performance please feel free to comment your ideas.
tldr: I want to create testdata but it does take too long because it is checking if the products already exist. Want to improve performance.
Here is my code:
public String insertProdukt(String name, Double preis, Integer kat_id) throws SQLException, ClassNotFoundException {
Connection connection = ConnectionUtils.createNewConnection();
// does the product exist?
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("select * from pro_produkte where pro_name=\"" + name + "\" AND pro_preis=\"" + preis + "\" AND pro_kat_id=\"" + kat_id + "\"");
if (resultSet.next()) {
//it does exist
System.out.println("Produkt: " + resultSet.getString("pro_name") + " existiert bereits");
} else {
//it dosen't -> insert into database
String sql = "Insert INTO pro_produkte (pro_name, pro_preis, pro_kat_id)"
+ "VALUES (\"" + name + "\", \"" + preis + "\", \"" + kat_id + "\")";
statement.executeUpdate(sql);
System.out.println("Produkt: " + name + " erstellt");
}
resultSet.close();
statement.close();
connection.close();
return null;
}
Thanks!
Instead of simply INSERT ..., use
INSERT IGNORE ...
And have a UNIQUE (or PRIMARY) that will catch the "duplicate".
INSERTing one row at a time is about 10 times as slow as inserting 100 rows at a time. So, if you are generating them by code, do
INSERT IGNORE INTO t
(col1, col2, ...)
VALUES
(1,2,...),
(22,55,...),
... ;
Or
LOAD DATA LOCAL INFILE '...' IGNORE ...
if reading from a file.
First thing - do not open a connection for every insert, unless you are using a connection pool.
Second thing - use PreparedStatement. Not only will this save you from SQL injection, it will also make it faster because it will avoid repetitive parsing.
Third thing - use PreparedStatement.addBatch() and commit a batch every 5000 rows (or something like that). This implies you use the same Connection and PreparedStatement for all inserts.
Fourth thing - if you are only filling the database with test data and you know that your test data is unique, create index AFTER you insert all the records. It will be significantly faster.
Fifth thing - if you are using InnoDB, make sure you have enough buffer space to keep entire index in memory, and put the database on SSD (~30x faster than HDD).
If you can do it outside Java, you can use database's proprietary features for bulk loading, restoring from backups or snapshots. Check what features your database provides.
I use UCanAccess (4.0.2) to create a new file with a single table like this:
// Create a database + connect
DatabaseBuilder.create(FileFormat.V2010, new File(path));
Class.forName("net.ucanaccess.jdbc.UcanaccessDriver");
conn = DriverManager.getConnection("jdbc:ucanaccess://" + path + ";singleconnection=true" ,"", "");
// Create table
String sql = "CREATE TABLE Test (id AUTOINCREMENT PRIMARY KEY, value CHAR(1) NOT NULL)";
conn.createStatement().executeUpdate(sql);
The code works but the resulting table seems to be incomplete/flawed. Trying to copy it in Access results in an error:
'' is not a valid name...
Doesn't seem to be a big problem as saving the table from the design view solves it.
Any idea why that happens and how to avoid it?
This is related to a confirmed issue with Jackcess versions 2.1.8 and older. A fix has been applied to Jackcess that will be included in the next release (tentatively 2.1.9). Once a UCanAccess update is released that includes the Jackcess fix then the problem should go away.
In H2 there are two ways to create a new in-memory database. In the first, you explicitly create the database with a CREATE DATABASE.. SQL statement. In the other, if you attempt to connect to a non-existent database, H2 will simply create it. I've elected the first way because if I don't get some kind of error back how will I know to create the single table (with only two columns).
The problem is that H2 doesn't like he SQL I'm using and flags an error. This SQL statement:
String sql = "CREATE DATABASE Tickets, " + USER + ", " + PASS;
throws this exception:
org.h2.jdbc.JdbcSQLException: Syntax error in SQL statement "CREATE DATABASE[*] TICKETS, USERNAME, PASSWORD "; expected "OR, FORCE, VIEW, ALIAS, SEQUENCE, USER, TRIGGER, ROLE, SCHEMA, CONSTANT, DOMAIN, TYPE, DATATYPE, AGGREGATE, LINKED, MEMORY, CACHED, LOCAL, GLOBAL, TEMP, TEMPORARY, TABLE, PRIMARY, UNIQUE, HASH, SPATIAL, INDEX"; SQL statement:
Any idea about what going on in the above? Or, can you tell me how I can tell that the DB was auto-created so that I can proceed to create the table?
I don't believe that you're correct when you suggest that you can create a H2 database via SQL - I think that's your basic issue...
Just connect to your DB (and it's the jdbc URL that defines the database involved) and if you don't get an exception, carry on and use it. (Create your table, etc.)
I have a a project name drop-down list, and based on the selected value a schema drop-down list is generated.
After the schema list is generated, there is a 'choose file' option where a script name is chosen which is to be run on multiple schemas.
Both the project list values and the schema list values are stored in the database. There is another table in the database where the schema credentials are stored.
I want to know that how create connections on those multiple schemas, and how to run a script in multiple schemas selected from that schema drop-down list.
I have executed the code in one schema using iBatis framework using scriptrunner method.
Table 1: ProjectName
PROJECT_PK,
PROJECT_CODE,
PROJECT_NAME
Table2: ComponentName
COMPONENT_PK,
COMPONENT_CODE,
COMPONENT_NAME,
PROJECT_PK
Table 3: SchemaName (This table contains the credentials of Other Schemas)
SCHEMA_PK,
SCHEMA_NAME,
PASSWORD,
ORACLE_SID,
HOST_NAME,
PORT_ID
Table 4: PROJECT_DETAIL
PROJECT_DETAIL_PK,
COMPONENT_PK,
SCHEMA_PK
Table5: COMPONENT_DETAILS
COMPONENT_DETAILS_PK,
PROJECT_PK,
SCHEMA_PK
I am attaching the scenario image.
I propose you create a 'super-schema' with the appropiate grants on every other schema, and create your JDBC connection for this 'super-schema'. This will make you need some tampering with the sql script - you will need to write it with some marker to easily substitute the schema, something like
ALTER TABLE ${SCHEMA_MARKER}.INVOICES ADD CLIENT_ADRRESS Varchar2(35);
And your java code would substitute it for the schema you are targeting. With some imagination you can extend this idea to execute bulk DDL scripts on all your schemas.
By the way, I understand by your question that you have many schemas with the same structure. I was forced once to work with such a legacy structure, and so I know there is a lot of momentum in corporate structures; nonetheless I must recommend you to redesign such system. Think for example in creating materialized views on your super-schema, replicating your schema structure but with a new field in every table primary key (this new field would fill the gap that made whomever did it separe the data in many schemas for starters).
This will surely make your query cache suffer a lot less, and will make easier the development of any new software that needs to work with that 'distributed' data.
In Oracle, a schema is basically a user. So to create a table FOO in the schema SX, you just login as user SX and execute create table FOO (...) (without specifying a schema). The user == schema convention in Oracle will make sure this works.
To login as user SX, get hold of your DataSource and use getConnection(String username, String password) instead of the default getConnection()
Alternative ways are to add placeholders in your script that you process to generate valid SQL first. See the answer by Jorge_B for an example.
Lastly, you can change the default schema using ALTER SESSION (see Default Schema in Oracle Connection URL). The problem with this approach is that you must restore the schema before closing the connection when using a web container with a DataSource - in this scenario, connections are never really closed, so the next piece of code asking for a connection will get one with an unexpected default schema -> hard to find errors.
Here is the DAO class to create multiple schema connection on the same database at run time.
We need to enter the schema names at run time through and call that in some servlet.
The schema details i.e username, password,host Id,port Id and SID are to be stored in the database tables though which the connections will be created.
The created connections are stored here in the list which can be used at a later point of time.
The credential DTO here is the object which maps to the database tables in the database and then
Connection conn=DBUtil.getConnection(constants.DB_DS_NAME);
Statement stmt=null;
stmt=conn.createStatement();
ResultSet rs= null;
Connection [] con=new Connection[schemaname.length];
int i,j;
String [] url=new String[schemaname.length];
String [] username=new String[schemaname.length];
String [] password=new String[schemaname.length];
List<CredentialDTO> creDTOlist=new ArrayList<CredentialDTO>();
String query1="insert into dba_project_master VALUES(9,'abc','abc','abc','abc',40)";
String query2="CREATE TABLE EMP(EMPNO NUMBER(4,0) NOT NULL ENABLE,ENAME VARCHAR2(10 BYTE),JOB VARCHAR2(9 BYTE), SAL NUMBER(7,2),DEPTNO NUMBER(2,0))";
try
{
for(i=0;i<schemaname.length;i++){
String query=" select * from dba_schema_details where schema_name="+DBUtil.enquoteString(schemaname[i]);
rs=stmt.executeQuery(query);
while(rs.next()){
CredentialDTO creDTO=new CredentialDTO();
creDTO.setSCHEMA_PK(rs.getString("SCHEMA_PK"));
creDTO.setSCHEMA_NAME(rs.getString("SCHEMA_NAME"));
creDTO.setPASSWORD(rs.getString("PASSWORD"));
creDTO.setORACLE_SID(rs.getString("ORACLE_SID"));
creDTO.setHOST_NAME(rs.getString("HOST_NAME"));
creDTO.setPORT_ID(rs.getString("PORT_ID"));
creDTOlist.add(creDTO);
}
}
System.out.println("creDTOlist size:"+creDTOlist.size());
//create URL for the schema name
int m=creDTOlist.size();
Iterator<CredentialDTO> LItr= creDTOlist.iterator();
String [] username1=new String[m];
String [] password1=new String[m];
i=0;
while(LItr.hasNext()){
System.out.println("iteration "+i);
CredentialDTO temp = LItr.next();
String URL="jdbc:oracle:thin:#"+temp.getHOST_NAME()+":"+temp.getPORT_ID()+":"+temp.getORACLE_SID();
System.out.println("URL:"+URL);
username1[i]=temp.getSCHEMA_NAME();
System.out.println("iteartion "+i+" username="+username1[i]);
password1[i]=temp.getPASSWORD();
System.out.println("iteartion "+i+" password="+password1[i]);
Class.forName("oracle.jdbc.OracleDriver");
con[i]=DriverManager.getConnection(URL, username1[i], password1[i]);
System.out.println("Connection Name:" +con[i]);
Statement st1=con[i].createStatement();
con[i].setAutoCommit(false);
st1.addBatch(query1);
st1.addBatch(query2);
int [] update=st1.executeBatch();
i++;
}
}
catch(Exception ex){
ex.printStackTrace();
}finally
{
if (conn != null) try{conn.close();} catch(SQLException ignore) {}
if (stmt!= null) try{stmt.close();} catch(SQLException ignore) {}
if (rs != null) try{rs.close();} catch(SQLException ignore) {}
}
return creDTOlist;
}
}
I am using Java with Microsoft Access through an ODBC driver. When I insert a duplicate entry for primary key it gives me an error: java.sql.SQLException: General error. I want to show a message to the user that this record already exists, but I think that this exception can be thrown by ODBC in some other cases also. So I found that there are error codes against each message (ref), but I found no error code for primary key violation. Can anyone tell me what error code is for primary key violation for ODBC with MS Access?
Here is basic code
String qry = "INSERT INTO customers VALUES ('" + txtReg.getText()
+ "' ,'" + txtName.getText() + "', '" + txtCity.getText() + "' ,'" + txtCell.getText() + "')";
try {
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
Connection con = DriverManager.getConnection("jdbc:odbc:MyDB");
Statement st = con.createStatement();
st.executeQuery(qry);
st.close();
con.close();
} catch (ClassNotFoundException ex) {
JOptionPane.showMessageDialog(this, "Error: " + ex, "Error!", JOptionPane.ERROR_MESSAGE);
} catch (SQLException ex) {
JOptionPane.showMessageDialog(this, "Error: " + ex, "Error!", JOptionPane.ERROR_MESSAGE);
}
These txtName and so on are JTextFields. Here is complete Stack trace
connected
st created
Error code: 0
SQLState: S1000
Messsage: General error
java.sql.SQLException: General error
at sun.jdbc.odbc.JdbcOdbc.createSQLException(JdbcOdbc.java:6986)
at sun.jdbc.odbc.JdbcOdbc.standardError(JdbcOdbc.java:7114)
at sun.jdbc.odbc.JdbcOdbc.SQLExecDirect(JdbcOdbc.java:3110)
at sun.jdbc.odbc.JdbcOdbcStatement.execute(JdbcOdbcStatement.java:338)
at sun.jdbc.odbc.JdbcOdbcStatement.executeUpdate(JdbcOdbcStatement.java:288)
at gui.InsertFileCode.btnInsertActionPerformed(insertFileCode.java:399)
The problem is in this line st.executeQuery(qry);
executeQuery(query) method is used mostly for SELECT statement and it returns the in form of ResultSet object.
Sence the statement is INSERT statement you have to use executeUpdate(query) , this method is generally used by INSERT, UPDATE, And DELETE statements. When table updated successfully then it returns 1.
For example
int result = st.executeUpdate(qry);
System.out.println(result);
UPDATE:
Due to the comments, I've fegured out you have another problem rather than the SQL statement. You must beware of when using java to Ms Access, you're actually connecting to a middleware server, so that, you must expect un-expected exception(s) while running the SQL statement, for example:
CRATE TABLE FOO (ID varchar (50) NOT NULL , NAME varchar (255) DEFAULT NULL)
This query runs on SQLite and MySQL (maybe SQL Server too as I didn't test it), gives Syntex error on Access, as DEFAULT NULL should be removed for running the statement.A nd maybe there are many other problem you have to prepare facing it with Access "database" file.
So, I am telling you to leave it, MS Access is suitable for its users, not for us as a programmer, we have to find best-general way because we must consider that some user uses this application that don't know anything neither about Programming Language nor Database.
Then, what should I do?
I am not an expert in database, but take my advice :
If your application need to share its database: MySQL, Oracle and SQL Server used for that purpose.
If your application is used only for some purposes and not need to share its records to other users, use an actually serverless database engine such as SQLite. This seems to be the best option for you as it's a file like Access, only needs an external driver for Java, see this.
I think there is a FireFox extension for designing the SQLite database if you search on google maybe you find it.