I'm building a Tomcat app using MySQL. I'm using a DAO pattern to talk to the database. I'm having trouble with where to initialize fields within the objects.
Mostly to save typing... I am using EclipseLink JPA to Generate model Entities from Tables. I am writing the DAO code by hand. I was writing the model by hand but this seemed easier.
The problem I am having is with writing the object back to the database will all the default nulls. For example, this table contains a bunch of id columns. Not all of them have context for every instance of the object. It seems inefficient to create the object and have to set all the fields to zero in order to be ale to save them to the database. If they have not context, I'd like to just leave them alone. i.e. I only want to set the fields that have context for what I am doing at that moment.
It seems best to use use the constructor in the model class to initialize them. But if I do that, EclipseLink will overwrite them the next time I generate the model.
Having the DAO update method check for nulls and set them to zero seems like a kludge. I suppose I could also use a factory to create and initialize the model class.
But I wonder if I am over thinking this... Any of these solutions would work. But there must be an accepted pattern for this.
How should I approach it?
Thanks
Model is just getters and setters. Contructor is empty.
Excerpts of code are below...
Notice notice = new Notice();
notice.setEvent("Welcome");
notice.setUserid(user.getId());
noticeDao.updateNotice(notice);
DAO:
//this seems inefficient
if(notice.getTravid() == null) notice.setTravid(0L);
if(notice.getBusid() == null) notice.setBusid(0L);
if(notice.getSaveid() == null) notice.setSaveid(0L);
if(notice.getTargid() == null) notice.setTargid(0L);
if(notice.getTestmode() == null) notice.setTestmode(false);
String SQLupdate = "UPDATE notices SET "
+ "userid = ?, "
+ "travid = ?, "
+ "busid = ?, "
+ "saveid = ?, "
+ "targid = ?, "
+ "testmode = ?, "
+ "event = ?, "
+ "status = ?, "
+ "error = ?, "
+ "created = ?, "
+ "modified = ?, "
+ "log = ? "
+ "WHERE id = ?";
ps = conn.prepareStatement(SQLupdate);
ps.setLong(1, notice.getUserid());
ps.setLong(2, notice.getTravid());
ps.setLong(3, notice.getBusid());
ps.setLong(4, notice.getSaveid());
ps.setLong(5, notice.getTargid());
ps.setBoolean(6, notice.getTestmode());
ps.setString( 7, notice.getEvent());
ps.setString( 8, notice.getStatus());
ps.setString( 9, notice.getError());
ps.setObject(10, notice.getCreated());
ps.setObject(11, notice.getModified());
ps.setString(12, notice.getLog());
ps.setLong( 13, notice.getId());
ps.executeUpdate();
DB:
CREATE TABLE `notices` (
`id` int(20) unsigned NOT NULL AUTO_INCREMENT,
`userid` int(20) unsigned NOT NULL DEFAULT '0',
`travid` int(20) unsigned NOT NULL DEFAULT '0',
`busid` int(20) unsigned NOT NULL DEFAULT '0',
`saveid` int(20) unsigned NOT NULL DEFAULT '0',
`targid` int(20) unsigned NOT NULL DEFAULT '0',
`testmode` tinyint(1) DEFAULT '0',
`event` varchar(40) DEFAULT NULL,
`status` varchar(20) DEFAULT 'Pending',
`error` varchar(255) DEFAULT NULL,
`created` datetime DEFAULT NULL,
`modified` datetime DEFAULT NULL,
`log` text,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=49 DEFAULT CHARSET=utf8
The database table is like this:
ID
Generically, the code looks like this:
Just to address your specific question, isn't there a way to tell eclipselink to set default values of the fields?
But there may be a deeper problem, with your database design. Are the *id fields not foreign key? They should be. And if they are foreign keys, then their value in database should be null, not 0L for rows where the specific fields do not have context.
At even deeper lever, if most of them don't have context in most rows -- that is, not being in context is an exception rather then norm -- then perhaps your database design itself is not good. You might be designing a universal table and will benefit from breaking the single table into multiple.
Thanks for the great input. The solution I settled on was just to use the generic setObject for all data types. i.e.
ps.setLong(1, notice.getUserid());
becomes
ps.setObject(1, notice.getUserid());
MySQL seems perfectly happy with that. NULLs are not a problem. And it's a lot less work.
Related
I inherited an old Servlet/JSP website, which has been updated to Java 1.8.0_201 and MySQL 5.7.28. Recently, I started to get this error when adding new records to the database:
Generated keys not requested. You need to specify Statement.RETURN_GENERATED_KEYS to Statement.executeUpdate(), Statement.executeLargeUpdate() or Connection.prepareStatement().
I googled what was going on, and found that I need to add Statement.RETURN_GENERATED_KEYS to my Statement.executeUpdateQuery, so I did. However, I still get the error. The updated code, and the error occurs at the statement result = stmt.getGeneratedKeys();:
stmt = con.createStatement();
switch(queryType) {
case INSERT_QUERY:
stmt.executeUpdate(query, Statement.RETURN_GENERATED_KEYS);
int autoIncKey = -1;
result = stmt.getGeneratedKeys();
if (result.next()) {
autoIncKey = result.getInt(1);
}
rows = stmt.getUpdateCount();
svr.setGeneratedKey(autoIncKey);
obj.setGeneratedKey(autoIncKey);
svr.setRows(rows); //Insert/Update/Delete
if (rows > 0)
svr.setSuccess(true);
else
svr.setSuccess(false);
break;
However, the insert works and the data is put into the database.
I then thought I should update the Mysql Connector library, so I updated from version 5.4 to mysql-connector-java-8.0.18.jar. Still getting the same error.
I am not using any prepared statements, just a String for the query text. This is the query string:
INSERT INTO Flights(rocket_name, weight, angle, baseline, egg_id, shockcord_id, notes, date, rocketModelID, mission_specialists, flight_engineers, teacher_id) VALUES ('asdfasdfasd', 98.0, 60.0, 60.0, 2, 2, 'sfdg sfdg sdg sfdg sdfg sfdg sfdg', '2020-01-07', 4,'asdfasdf', 'asdfasdfas', 13);
The table definition for Flights:
| Flights | CREATE TABLE `Flights` (
`flight_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`teacher_id` int(11) NOT NULL DEFAULT '0',
`egg_id` int(10) unsigned NOT NULL DEFAULT '0',
`shockcord_id` int(10) unsigned NOT NULL DEFAULT '0',
`rocket_name` varchar(100) NOT NULL DEFAULT '',
`weight` decimal(10,4) unsigned NOT NULL DEFAULT '0.0000',
`angle` decimal(10,5) NOT NULL DEFAULT '0.00000',
`baseline` decimal(10,5) NOT NULL DEFAULT '0.00000',
`date` date NOT NULL DEFAULT '0000-00-00',
`rocketModelID` int(11) unsigned NOT NULL DEFAULT '0',
`flight_engineers` varchar(100) NOT NULL DEFAULT '',
`mission_specialists` varchar(100) NOT NULL DEFAULT '',
`notes` text,
PRIMARY KEY (`flight_id`),
FULLTEXT KEY `search1` (`mission_specialists`),
FULLTEXT KEY `search2` (`flight_engineers`),
FULLTEXT KEY `search3` (`flight_engineers`,`mission_specialists`)
) ENGINE=MyISAM AUTO_INCREMENT=562 DEFAULT CHARSET=latin1
I am not sure how to proceed. Any suggestions would be greatly appreciated!
Mark
Recommend changing your code as follows:
Name the key column
Get the row count from the executeUpdate call
Don't call getGeneratedKeys() if no rows were inserted
rows = stmt.executeUpdate(query, new String[] { "flight_id" });
int autoIncKey = -1;
if (rows > 0) {
result = stmt.getGeneratedKeys();
if (result.next()) {
autoIncKey = result.getInt(1);
}
}
svr.setGeneratedKey(autoIncKey);
obj.setGeneratedKey(autoIncKey);
svr.setRows(rows); //Insert/Update/Delete
svr.setSuccess(rows > 0);
Though, really, a single INSERT statement using VALUES will always insert exactly one row, so checking row count is entirely unnecessary. If the row wasn't inserted, an exception would have been thrown, so your code could be reduced to:
stmt.executeUpdate(query, new String[] { "flight_id" });
try (ResultSet result = stmt.getGeneratedKeys()) {
result.next();
int autoIncKey = result.getInt(1);
svr.setGeneratedKey(autoIncKey);
obj.setGeneratedKey(autoIncKey);
}
svr.setRows(1);
svr.setSuccess(true);
Go easy on me, middle school teacher taking a CS class. I've got a Java program that asks for user name, height, weight, does some calculations and gives results to the user. I now need to store this data in a database. I can get the data to store until I start using primary and foreign keys.
Here is the error I can't figure out:
Error: java.sql.SQLIntegrityConstraintViolationException: The statement was aborted because it would have caused a duplicate key value in a unique or primary key constraint or unique index identified by 'SQL180429151131780' defined on 'USERPROFILE'.
Here is my table:
drop table stayfitapp.userdata;
drop table stayfitapp.userprofile;
drop schema stayfitapp restrict;
create schema stayfitapp;
create table stayfitapp.userprofile
(
profileName varchar(255) not null primary key,
profileGender varchar(255) not null
);
create table stayfitapp.userdata
(
profileAge double not null,
profileWeight double not null,
profileHeight double not null,
profileWaistCircumference double not null,
profileHipCircumference double not null,
profileName varchar(255),
foreign key (profileName) references stayfitapp.userprofile(profileName)
);
Here is the section of the "app" that writes to the table...
public void save(){
try {
String query = "insert into stayfitapp.userprofile" + "(profileName, profileGender)" + "values" + "(?,?)";
String query2 = "insert into stayfitapp.userdata" + "(profileAge, profileWeight, profileHeight, profileWaistCircumference, profileHipCircumference)" + "values" + "(?,?,?,?,?)";
Connection myConnection = DriverManager.getConnection("jdbc:derby://localhost:1527/stayfitDB2", "username", "password");
Statement myStatement = myConnection.createStatement();
//Statement myStatement2 = myConnection.createStatement();
PreparedStatement prepared = myConnection.prepareStatement(query);
prepared.setString(1, profileName);
prepared.setString(2, profileGender);
PreparedStatement prepared2 = myConnection.prepareStatement(query2);
prepared2.setDouble(1, profileAge);
prepared2.setDouble(2, profileWeight);
prepared2.setDouble(3, profileHeight);
prepared2.setDouble(4, profileWaistCircumference);
prepared2.setDouble(5, profileHipCircumference);
int rowsAffected = prepared.executeUpdate();
int rowsAffected2 = prepared2.executeUpdate();
if(rowsAffected==0)
{
System.out.println("Warning: User data did not save!");
}
else
{
System.out.println("User info saved!");
}
}
catch(SQLException e)
{
System.out.println("Error: "+e.toString());
}
Your save() method will attempt to add the user to the stayfitapp.userprofile table. This table has a field called profileName. profileName is the "primary key" so no duplicate values are allowed.
The error that you are getting is saying that you cannot add(insert) the record to the table because the table already has a record with the same name.
Does your program work okay if you use a different name each time?
You will need to add some logic to your program to deal with the scenario where the profileName already exists in the table. This will probably involve deleting or updating the existing record.
This is the problem.
insert into stayfitapp.userprofile"
+ "(profileName, profileGender)" + "values" , etc
You have nothing to check to see if a record already exists. Something like this would work better.
insert into stayfitapp.userprofile
profileName, profileGender
select distinct ?, ?
from someSmallTable
where not exists (
select 1
from stayfitapp.userprofile
where profileName = ?
)
The someSmallTable bit depends on your database engine, which you didn't specify.
I ended up writing a method to check if the username was already in the profile table. If the username was a duplicate I only wrote to the data table. If the username was new I wrote to both tables.
Thank you for your help! I'm sure there was a more efficient method (figuratively and literally) but I'm on to my final project and nearly surviving an actual CS class.
I'm trying to use ActiveJDBC with HSQLDB:
Users.sql
CREATE TABLE users (
"ID" INTEGER GENERATED BY DEFAULT AS SEQUENCE seq NOT NULL PRIMARY KEY,
FIRST_NAME VARCHAR(100) NOT NULL,
LAST_NAME VARCHAR(100) NOT NULL
);
User.java
#IdName("ID")
public class User extends Model {....}
Main.java
User u = new User();
u.setFirstName("first_name");
u.setLastName("last_name");
u.save();
And when I try to save new row I have the following exception:
org.javalite.activejdbc.DBException: java.sql.SQLSyntaxErrorException: user lacks privilege or object not found: id, Query: INSERT INTO users (first_name, last_name) VALUES (?, ?), params: first_name,last_name
I think that problem in lower case in insert query. How I can fix this problem?
After googling for this HSQL exception message, I threw this code together that actually works. It uses ActiveJDBC, works with plain query and also works with instrumented model:
Base.open("org.hsqldb.jdbc.JDBCDriver", "jdbc:hsqldb:file:./target/tmp/hsql-test", "sa", "");
String create = "CREATE TABLE people (\n" +
" ID INTEGER GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1) NOT NULL,\n" +
" FIRST_NAME VARCHAR(100) NOT NULL,\n" +
" LAST_NAME VARCHAR(100) NOT NULL\n" +
");";
Base.exec(create);
Base.exec("INSERT INTO people (first_name, last_name) VALUES (?, ?)", "John", "Doe");
System.out.println("===>" + Base.findAll("select * from people"));
Person p = new Person();
p.set("first_name", "Jane", "last_name", "Doe").saveIt();
System.out.println(Base.findAll("select * from people"));
System.out.println(Person.findAll());
Base.close();
As you can see, the code that creates a table is a bit different, especially around the ID column.
The model looks like this:
#IdName("ID")
public class Person extends Model {}
Basically, you had the following issues with your code:
Definition of ID column with double quotes
ID column needs to be defined as IDENTITY (do not hold my feet to the fire, I'm no HSQL expert, but t works)
The model needs to overwrite the #IdName("ID"), since ActiveJDBC defaults to lower case id, unless you change that in the table (why not?)
Keep in mind, that some things may not work, since HSQL is not on a list of supported databases.
I hope this helps!
CREATE TABLE users (
ID INTEGER GENERATED BY DEFAULT AS SEQUENCE seq NOT NULL PRIMARY KEY,
FIRST_NAME VARCHAR(100) NOT NULL,
LAST_NAME VARCHAR(100) NOT NULL
);
I have a table with unique index to eliminate duplicates (simplified example)
CREATE TABLE `domain` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`subdomain` VARCHAR(255) NOT NULL,
`domain` VARCHAR(63) NOT NULL,
`zone` VARCHAR(63) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE INDEX `UNIQUE` (`subdomain` ASC, `domain` ASC, `zone` ASC),
ENGINE = InnoDB;
I insert a lot of rows and i need to get primary keys returned (for other one-to-many inserts).
My problem is, that I insert a lot of duplicates and I need those keys returned too.
This is my solution which works, but isn't there more simple solution? With this I cannot use batch inserts and I want this to be most efficient.
PreparedStatement selectDomain = connection.prepareStatement("SELECT id FROM domain WHERE subdomain = ? AND domain = ? AND zone = ?");
PreparedStatement insertDomain = connection.prepareStatement("INSERT INTO domain(subdomain, domain, zone) VALUES (?,?,?)", Statement.RETURN_GENERATED_KEYS);
public int insertDomain(String subdomain, String domain, String zone) throws SQLException {
int domainId = 0;
selectDomain.setString(1, subdomain);
selectDomain.setString(2, domain);
selectDomain.setString(3, zone);
ResultSet resultSet = selectDomain.executeQuery();
if (resultSet.next()) {
domainId = resultSet.getInt(1);
} else {
insertDomain.setString(1, subdomain);
insertDomain.setString(2, subdomain);
insertDomain.setString(3, subdomain);
insertDomain.executeUpdate();
resultSet = insertDomain.getGeneratedKeys();
if (resultSet.next()) {
domainId = resultSet.getInt(1);
}
}
selectDomain.clearParameters();
insertDomain.clearParameters();
}
As I understand its not so easy approach for using batch execution. Your apporach is the best way to get the auto generated keys. There are few limitations of JDBC driver and it varies version to version, where getGeneratedKeys() works for single entry.
Please look into below links, it may help you :-
How to get generated keys from JDBC batch insert in Oracle?
http://docs.oracle.com/database/121/JJDBC/jdbcvers.htm#JJDBC28099
You could modify your INSERT to be something like this:
INSERT INTO domain (subdomain, domain, zone)
SELECT $subdomain, $domain, $zone
FROM domain
WHERE NOT EXISTS(
SELECT subdomain, domain, zone
FROM domain d
WHERE d.subdomain= $subdomain and d.domain=$domain and d.zone=$zone
)
LIMIT 1
Where $subdomain, $domain, $zone are the tag (properly quoted or as a placeholder of course) that you want to add if it isn't already there. This approach won't even trigger an INSERT (and the subsequent autoincrement wastage) if the tag is already there. You could probably come up with nicer SQL than that but the above should do the trick.
If your table is properly indexed then the extra SELECT for the existence check will be fast and the database is going to have to perform that check anyway.
I am working on a project with MySQL database Java GUI.
This is my SQL script
CREATE TABLE takenservice (
ResvID INT UNSIGNED NOT NULL,
ServID INT UNSIGNED DEFAULT 0 NOT NULL,
ServUsedDate DATETIME NOT NULL
, PayStat VARCHAR(6) DEFAULT Unpaid NOT NULL,
ServRecBy VARCHAR(7) NOT NULL,
PayRecBy VARCHAR(7),
Quantity SMALLINT DEFAULT 1 NOT NULL,
PRIMARY KEY (ResvID, ServID, ServUsedDate));
Alter table takenservice
add constraint foreign key (ResvId) references reservation (ResvID);
Alter table takenservice
add constraint foreign key (ResvId) references reservation (ResvID);
I created the entity classes with Netbeans 7.0 and created 4 classes:
Service
Reservation
ServiceTaken
ServiceTakenPK
but I cant enter values to the table and it gives me an error that I am trying to enter null values in as primary keys.
I have found the bug actually in Netbeans forum which is http://netbeans.org/bugzilla/show_bug.cgi?id=105084.
and accordingly I have added the following methods to the class ServiceTaken
public int getResvID() {
return takenservicePK.resvID;
}
public int getServID() {
return takenservicePK.servID;
}
public Date getServUsedDate() {
return takenservicePK.servUsedDate;
upon inserting a new entry in the table I get this error :
Error Code: 1048
Call: INSERT INTO takenservice (PayRecBy, ServRecBy, Quantity, PayStat, ServUsedDate, ResvID, ServID) VALUES (?, ?, ?, ?, ?, ?, ?)
bind => [ddd, ddd, 0, ddd, null, null, null]
What I can do to fix this ? Please help me with this I am running out of time =/
Please try using
System.out.println(yourqueryexecution stmt);
then check whether the statement can be executed in the DB;
Find whether it sends all the values to the db;
hope this works..