The DB2-express is throwing an error that seems impossible, when JPA tries to persist an entity:
ReportingSQLException: "ITEMID" is not valid in the context where it is used..
INSERT INTO NULLID."DynamicDatabaseTable" ("colname", "rownumber", "value")
VALUES (?, ?, ?) [params=?, ?, ?]
Since ITEMID is not used in the query above, how can this error be? The ITEMID is an auto-identity-generated column.
I have tried executing manually the query and it works OK, so anybody know what's all about? Even after lot of googling, I'm totally clueless here.
The database table in DB2 is fine, I have triple check:
"itemid" BIGINT NOT NULL GENERATED ALWAYS AS IDENTITY ( START WITH 1...),
"rownumber" BIGINT NOT NULL,
"colname" CHAR(30 OCTETS) NOT NULL,
"value" VARCHAR(254 OCTETS)
I had created a simple java "JPA Entity from a table", by using the Eclipse wizard, and I've triple check the class is fine.
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="\"itemid\"", unique=true, nullable=false)
private long itemid;
#Column(name="\"rownumber\"", nullable=false)
private long rownumber;
#Column(name="\"colname\"", nullable=false, length=30)
private String colname;
#Column(name="\"value\"", length=254)
private String value;
I have also restarted the whole computer just in case, but nothing... And I'm using most recent versions, clean installed couple of weeks ago.
"itemid" is not equal itemid -- SQL identifiers are by default converted to upper case if not enclosed in double quotation marks. According to your DDL you created the table with lowercase column names. Apparently, JPA does not handle that properly and somewhere in the generated code uses unquoted itemid, which gets converted to upper case and, not surprisingly, the column cannot be found in the table.
There is no reason to use case sensitive identifiers in SQL, so I suggest you don't do that to avoid these kinds of problems.
Related
I know there are dozens of tutorials for how to do this across just as many websites, but this is my first time trying to connect a database table to a UI, so when the version of Spring Boot/MyBatis/Vaadin, for example, are different than the one I'm working with, or they use JPA or JDBC instead of MyBatis, I have no idea how to change it to work with my specific situation.
When people say "it's no different than any other method of doing it with " that doesn't help AT ALL, since, as I stated earlier, I've never done it before. Annotations and classes in the code examples of a tutorial get removed and deprecated with every new version with no clear explanation of how to change it to work with the newer version. I've been researching the various APIs (Spring Boot, Vaadin, MyBatis) for about a month and have a vague understanding of what each one does but not how they work together to achieve the desired result of making a UI for a database. I'm just getting really frustrated at how a single deprecated annotation or class in a tutorial can bring the whole thing crashing down. I know that was long-winded but I just wanted you all to understand where I'm coming from. I'm not particularly attached to any single API, just whatever is easiest.
My current dependencies are:
- Maven : 4.0.0
- Spring Boot: 2.1.2.RELEASE
- Vaadin: 12.0.4
- MyBatis Spring Boot Starter: 2.0.0
I got the starter package from Spring Initializr and added the MyBatis dependency later.
I have a PostgreSQL 10.5 database with 17 tables that will eventually be a UI for a store manager to use for things like looking at received inventory shipments, the hours an employee worked, and other tasks.
My database is named 'store', user: 'store', password: 'store' (if it matters).
For example, these are a few of my tables:
CREATE TABLE IF NOT EXISTS supplier (
id SERIAL,
brand VARCHAR(30) NOT NULL,
phone VARCHAR(15) NOT NULL,
address VARCHAR(100) NOT NULL,
CONSTRAINT pk_supplier PRIMARY KEY (id)
);
CREATE TABLE IF NOT EXISTS shipment (
id SERIAL,
shipdate DATE NOT NULL,
shiptime TIME NOT NULL,
status VARCHAR(10) DEFAULT 'arrived' NOT NULL,
sid INT NOT NULL,
CONSTRAINT pk_shipment PRIMARY KEY (id),
CONSTRAINT fk_shipment_supplier FOREIGN KEY (sid)
REFERENCES supplier(id)
);
CREATE TABLE IF NOT EXISTS shipmentcontains (
shipid INT NOT NULL,
iid INT NOT NULL,
quantity INT NOT NULL,
price DEC(6,2) NOT NULL,
CONSTRAINT pk_shipmentcontains PRIMARY KEY (shipid, iid),
CONSTRAINT fk_shipmentcontains_shipment FOREIGN KEY (shipid)
REFERENCES shipment(id),
CONSTRAINT fk_shipmentcontains_item FOREIGN KEY (iid)
REFERENCES item(id)
);
CREATE TABLE IF NOT EXISTS item (
id SERIAL,
itemtype VARCHAR(25) NOT NULL,
itemsize VARCHAR(10) NOT NULL,
price DEC(5,2) NOT NULL,
sid INT NOT NULL,
CONSTRAINT pk_item PRIMARY KEY (id),
CONSTRAINT fk_item_supplier FOREIGN KEY (sid)
REFERENCES supplier(id)
);
CREATE TABLE IF NOT EXISTS employee (
id SERIAL,
lastname VARCHAR(40) NOT NULL,
firstname VARCHAR(40) NOT NULL,
hourlywage DEC(4,2),
manager BOOLEAN DEFAULT false NOT NULL,
CONSTRAINT pk_employee PRIMARY KEY (id)
);
If someone can give me a code example of how to just get one of those to show in a Grid, I'm sure I can figure out how to do the rest of it. I have the connection details in my application.properties file, but I've seen that with newer versions of MyBatis this isn't needed and annotations such as #Update can be used on the SQL statements to replace that. Also, in plain English, what the heck is a Spring Bean? I hope that wasn't too long..or not long enough.
EDIT: Current version of Vaadin 12 is 12.0.4
You are asking quite a lot, so I will try to touch everything a little and nothing too detailed. I hope this helps you getting the ball rolling.
First off, you will need a java class with all fields that you have in the supplier table, annotated with #Entity. The #Table annotation lets you define the Db table name, and it is not necessary if the table is called the same as the class (case insensitive):
#Entity // javax.persistence
#Table(name = "supplier") // javax.persistence
public class Supplier {
#Id // javax.persistence
private Long id;
private String brand;
private String phone;
private String address;
public Supplier(){
}
// public getters and setters for all fields
// omitted for brevity
}
Now that you have a class for your table, you can start with creating a Vaadin Grid for it. This can be done the easiest with Grid<Supplier> supplierGrid = new Grid<Supplier>(Supplier.class);.
Now to fill the grid with items (suppliers). This is done with supplierGrid.setItems(allSuppliers);. But where do allSuppliers come from you ask?
They can be fetched using a Repository. Because the repository will be annotated with #Repository, its a spring component that can be automatically generated by spring and can be Injected/Autowired (i.e. in your view) using #Inject/#Autowired.
Then you simply call List<Supplier> allSuppliers = supplierRepository.findAll() and you have a list of all suppliers from your DB, that you now can put into the grid with the aforementioned supplierGrid.setItems(allSuppliers);
Any class where an instance of it can be injected by spring is a spring-bean, this includes classes annotated with either #Component, #Serivce or #Repository. Entities like Supplier can not automatically be injected by Spring, unless you define this is your #Configuration class:
/* Do this only if you want to inject a Supplier somewhere. */
#Bean
public Supplier supplier(){
/* define here how a default Supplier should look like */
return new Supplier();
}
I have the following embeddable class that contains an #Lob:
#Embeddable
public class EntityState {
private Integer version;
#Lob
#XmlJavaTypeAdapter(CharArrayAdapter.class)
private char[] xmlState;
...
}
I also have the following embeddable class that contains the above embeddable:
#Embeddable
public class EntityEvent {
#NotNull
private String note;
private EntityState entityState;
...
}
Finally, I have many entity classes that contain a property called history that is a list of EntityEvents. The following is an example:
#Entity
public class Company {
#NotNull
#ElementCollection
private List<EntityEvent> history;
...
}
When I deploy my application in GlassFish 4.1, EclipseLink creates the following tables in my Derby 10.11.1.1 database:
COMPANY
COMPANY_HISTORY
When I create a new Company, my application creates an EntityEvent and adds the EntityEvent to the Company history.
When I modify a Company, my application does the following:
Creates an EntityState object and sets the xmlState property to an XML representation of the unmodified entity.
Creates an EntityEvent object containing the above EntityState.
Adds the EntityEvent to the Company history.
The problem is that when I try to delete an entity that has a history with multiple EntityEvents I receive the following error:
Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.DatabaseException Internal Exception: java.sql.SQLSyntaxErrorException: Comparisons between 'CLOB (UCS_BASIC)' and 'CLOB (UCS_BASIC)' are not supported. Types must be comparable. String types must also have matching collation. If collation does not match, a possible solution is to cast operands to force them to the default collation (e.g. SELECT tablename FROM sys.systables WHERE CAST(tablename AS VARCHAR(128)) = 'T1')
Error Code: 20000 Call: DELETE FROM Company_HISTORY WHERE ((((((((((CHANGES = ?) AND (CLIENTTYPE = ?)) AND (CREATED = ?)) AND (IPADDRESS = ?)) AND (NOTE = ?)) AND (TYPE = ?)) AND (VERSION = ?)) AND (XMLSTATE = ?)) AND (CREATER_ID = ?)) AND (Company_ID = ?)) bind => [10 parameters bound]
I found a few references to the issue in the following links:
Hibernate - #ElementCollection - Strange delete/insert behavior
http://eclipse.1072660.n5.nabble.com/Customizing-delete-calls-before-updating-a-ElementCollection-td7312.html
I tried the #OrderColumn technique described in the above referenced stackoverflow article but this did not work in EclipseLink.
The solution that work for me was to add the EclipseLink nonstandard #CascadeOnDelete annotation to my entity as shown below:
#Entity
public class Company {
#NotNull
#ElementCollection
#CascadeOnDelete
private List<EntityEvent> history;
...
}
After performing this change and rebuilding my database, my COMPANY_HISTORY table has a new definition:
Without #CascadeOnDelete
ALTER TABLE COMPANY_HISTORY ADD CONSTRAINT CMPNYHISTORYCMPNYD FOREIGN KEY (COMPANY_ID) REFERENCES COMPANY (ID);
With #CascadeOnDelete
ALTER TABLE COMPANY_HISTORY ADD CONSTRAINT CMPNYHISTORYCMPNYD FOREIGN KEY (COMPANY_ID) REFERENCES COMPANY (ID) ON DELETE CASCADE;
The solution to my problem surprised me because it seems repetitive. My understanding is that JPA should delete all embeddables associated with an entity when the entity is deleted. The fact that EclipseLink has this nonstandard annotation as documented in the following link makes me think that EclipseLink has a bug and instead of fixing the bug created a new #CascadeOnDelete annotation so that the bug would be covered up by the databases cascading delete functionality.
http://www.eclipse.org/eclipselink/documentation/2.5/jpa/extensions/a_cascadeondelete.htm
So my question is why. Why does EclipseLink support the #CascadeOnDelete with #ElementCollection?
CascadeOnDelete is simply a feature that specifies that you have specified the "On Delete Cascade" option in your tables, so that JPA does not need to issue SQL to delete the corresponding references. This SQL can apply to any reference, which is why CascadeOnDelete works on an element collection mapping and any other referene mapping.
Your issue has to do with lob comparison limitation in your database, and since there isn't an ID field to uniquely identify element collection rows, this limitation interferes with the way EclipseLink tries to ensure it is only removing the required rows. If you were willing to add an order column to your table, why not just make the EntityEvent an Entity? Or you can customize EclipseLink as described here so that it uses the foreign key and an orderBy field or any combination of fields as a primary key to uniquely identify rows instead of including the lob field.
I have two classes, CalculatedValue and Price. Price has map of CalculatedValue. Each CalculableValue instance has name, value and couple of other fields.
Here is mapping I use to describe a dependency between Price and the CV:
#OneToMany(
cascade = CascadeType.ALL,
fetch = FetchType.EAGER
)
#JoinColumn(name = "priceId")
private Map<String, CalculatedValue> calculatedValues =
new TreeMap<String, CalculatedValue>();
No join table, just mapping by priceId column which refers to Price unique Id.
Here is how generated table looks like:
CREATE TABLE PUBLIC.CALCULATEDVALUE (
UNIQUEID BIGINT NOT NULL,
KEY VARCHAR(2147483647) NOT NULL,
PRICEID BIGINT,
VALUE DOUBLE NOT NULL,
CALCULATEDVALUES_KEY VARCHAR(2147483647),
PRIMARY KEY (UNIQUEID)
);
ALTER TABLE PUBLIC.CALCULATEDVALUE
ADD FOREIGN KEY (PRICEID)
REFERENCES TEST.PUBLIC.PRICE (UNIQUEID);
Everything is working, but I want to know if it possible to to this:
Avoid automatic "CALCULATEDVALUES_KEY" column creation. I already have this value stored in KEY column and it would be nice to avoid duplication and somehow give a hint to JPA.
Trigger cascade delete of calculable value for each removed price (in case I'm running SQL delete statement)
Will such mapping work in case I'll use Date as a key? Not for this particular field, but for a bunch of other ones it will be useful. Assuming the same OneToMany relationship.
Thank you in advance!
PS. I'm using latest version of EclipseLink & H2 as database.
PPS. Didn't want to store the calculable values in array since I need to often find it buy key in Java.
For info on Maps see,
http://en.wikibooks.org/wiki/Java_Persistence/OneToMany
and,
http://en.wikibooks.org/wiki/Java_Persistence/Relationships#Maps
and,
http://www.eclipse.org/eclipselink/documentation/2.4/jpa/extensions/a_cascadeondelete.htm#CIABIIEB
A few issues:
EclipseLink will use Hashtable by default for Map, if you want it to use TreeMap you need to define the field as TreeMap.
Do not give a #JoinColumn on a #OneToMany, this is only supported for advanced unidirectional #OneToMany, a normal #OneToMany should use a mappedBy and have an inverse #ManyToOne in the target entity. (this will fix your issue of the duplicate foreign key).
You need to specify the #MapKey for a map, otherwise it defaults to the id, which seems to be an integer here, not a string.
You can use #CascadeOnDelete in EclipseLink to cascade a delete on the database.
Using Open JPA 2.0, and database is DB2 9.7. I would like to like to create an entity which includes ID which is Primary Key, and auto generated Identity column and after creating the entity I need to display the ID generated. For this after persisting the entity, I am calling entity.getId().
In the database, I see below query getting executed
select ID from final table
(INSERT INTO WEB.USER (NAME, LOCATION) VALUES (?, ?))
Is there any alternate / better way to create an entity and return the generated ID? I would like to avoid "Select ID from final table", as this is expected to have performance impact.
This is how you retrieve auto generated ID from DB2 and there is nothing you can do about it. Other databases often require extra SQL query, so it's actually even better with DB2, which returns ID immedaitely. I don't think there is a faster way to so this.
Check out OPENJPA-736 where this optimization was implemented.
I am using Database sequence (#SequenceGenerator) to avoid "select ID from final table". This worked fine and #TableGenerator would also have sorted this issue.
I'm no SQL expert, but have a bug in an iPhone app where and UPDATE statement has no effect on the db.
I have been using the SQLlite manger plugin for FireFox to try dbug by repeatedly amending and running the UPDATE on the db. I also ran the statement thorough and SQL Validator which said it complied to the core SQL standard.
Can you spot anything wrong with the statement given below?
UPDATE sections
SET
title = 'What is acne ? ABC',
text = 'Pus on your face',
created = '2010-03-10 18:46:55',
modified = '2011-07-04 17:38:44',
position = 1,
condition_id = 4
WHERE id = 10;
There is some confusion and inconsistency in the way SQLite and the various implementations by Mozilla, Google, Adobe, and others handle numeric primary keys in databases whose tables were created outside of these implementations and where the primary keys were defined as an integer type but not as "INTEGER" [verbatim] -- that is, they were defined as INT or INT16 or INT32 etc.
INTEGER PRIMARY KEY in mothership SQLite is an alias for the rowid.
INT PRIMARY KEY in mothership SQLite is not an alias for the rowid.
A consortium member (or any implementor) may or may not follow this rule. (SQLite is in the public domain, of course.)
See section 2.0 here: http://www.sqlite.org/datatypes.html
and see section on RowId and Primary Key here: http://www.sqlite.org/lang_createtable.html#rowid
A PRIMARY KEY column only becomes an integer primary key if the
declared type name is exactly "INTEGER". Other integer type names like
"INT" or "BIGINT" or "SHORT INTEGER" or "UNSIGNED INTEGER" causes the
primary key column to behave as an ordinary table column with integer
affinity and a unique index, not as an alias for the rowid. [emphasis
added]
An implementor who does not follow the rule might not have even been aware that they were breaking the rule in the first place, since it is a "gotcha" arcane sort of rule. Anyway, what this means practically is that one implementation may treat a supplied value as an alias for the rowid and another implementation might not. If given the value 10, one might retrieve the tuple whose rowid = 10 and one might retrieve the tuple where the specified column's value = 10. This, of course, leads to spurious results in queries -- and they might look like perfectly good and plausible results but they are dead wrong.
Consider the following simple test: using flagship SQLite's utilities, not those provided by one of the implementors, execute the following DDL and DML statements; then, in your implementation, open the database and execute the DML statements again to compare the DML results:
CREATE TABLE TEST
("id" INT PRIMARY KEY, "name" text) -- ** NOTE "INT" not "INTEGER"
INSERT INTO TEST
(id, name)
VALUES
(7,'seven')
** *** N.B. THE ROWID OF THE ROW INSERTED ABOVE = 1 *** **
select rowid, id, name from test
result: 1 | 7 | seven
select * from TEST
result: 7 | seven
select * from TEST where id = 7
result: ????? [ymmv]
select * from TEST where id = 1
result: ????? [ymmv]
Depending on how the specific implementation treats an INT primary key the third select statement above (select * from TEST where id = 7) may return one row or it may return nothing!
If the implementation treats the INT PK as an alias for the row id, well, there is no row whose rowid = 7, and so it will return nothing. If the implementation treats the INT PK as a normal value, it will find the row.
Now, if you were to insert more rows into table TEST, you would eventually create a row whose rowid = 7. In one of these wayward implementations, when you use this where-clause -- where id = 7 --- you might think you were addressing the tuple whose id=7, but you'd actually be addressing the tuple whose rowid=7. You would get the wrong tuple and you might not realize it. Consider the possibilities when joining a child table to a parent table: the child table contains foreign key value of 7. What tuple does an inner join return from the parent table? It depends on whether the implementation honors the distinction between INT and INTEGER primary keys.
Last year, I documented this thoroughly for Adobe AIR, BTW, and also reported it on the SQLite news group. It is possible that some implementations have changed the behavior in the interim.
When creating SQLite tables, it is best to use INTEGER [verbatim] for primary keys, not any of the other recognized int types.
If your query is correct then You need to make sure about 2 things.
Have you written finalize_statement like this ?
sqlite3_finalize(selectStatement);
2.If you are testing in simulator. Are you sure you are checking database update in following path ?
/user/Libary/Application Support/iPhone Simulator/Your_Version_Number/Applications/YOUR_APPLICATION_GUID/Documents
Hope this help.
The only thing in the query that strikes me as worthy of further investigation is the question mark in the value for [title]. Remove it and see if that changes anything. Maybe it's being incorrectly parsed somewhere along the way as a parameter placeholder.