It appears that Spring Boot is not capable of interpreting SELECT query results when id is not unique.
For example, let us say that there is a table t in the database which has two columns c and c2. Table does not have a primary key, so both columns may be duplicated, but it has an unique constraint on combination of c and c2 columns so that rows can not be duplicated.
Here are sample data for this table:
c | c2
______
a | a1
a | a2
a | a3
b | a1
What I need to achieve in this table is to have one and only one b in the c column and zero to many as.
I defined a Spring's repository for this table as follows:
package P;
public interface tr extends org.springframework.data.repository.PagingAndSortingRepository<P.t, java.lang.String>
{
public java.util.List<P.t> findAllByc(java.lang.String c);
}
Here I specified that Spring should use a string for an table id. Although I do not use any of repository's "*ById" methods. This is just something what is required to be specified here whether or not I need it.
And the table is represented in the Java Persistence Application programming interface class t like this:
package P;
#javax.persistence.Entity()
#javax.persistence.Table(schema = "s")
public class t implements java.io.Serializable
{
#javax.persistence.Id()
public java.lang.String c;
public java.lang.String c2;
}
Here I marked column c as an id, because it is Spring's requirement, although, as I said, I do not use "*byId" methods, so, it is not important to me.
Now, in the repository I defined (or, to be precise, declared) a custom method findAllByc which, as the name suggests, should be able to find zero to many rows of the table t where c column matches the given string.
Unfortunately, this only works when I try to search for row with unique value in the c column, for example, findAllByc("b");. When the c column is not unique, this method behaves very strangely. It works, it finds correct number of rows, but, strange thing which it does is that it finds only the first row in the table that matches the given c value, and then it simply copies that row the number of times of the count of the rows that match given c value.
So, for example, for this sample table, if I execute findAllByc("a"); the result will be:
a|a1
a|a1
a|a1
So, as you can see from the sample table which I provided above, a1 value is written only once in the table where c column is equal to a.
So what Spring Boot does is that it simply copies the first row that matches into all other rows.
Why, on the Earh, Spring would do that??? This is very strange and unexpected behaviour.
This only occurs on the column which is marked as table's id. So, for example, if I declare findAllByc2 method, then everything works as expected, that is, if I call that method and give it, for example, a2 as parameter, it will find only one row, and if I give it a1 as a parameter, it will find both rows, and will not duplicate them, for example:
a|a1
b|a1
So, I know that the cause of the problem is that c is marked as id, and id is supposed to be unique, but that should be true only for "*ById" methods which construct the queries based on the id. I do not use these methods for this table. The method which I use is "Byc" method, which means that it will use c when constructing queries, and not id. In this case, c is the same as id, I know. But the difference between findById and findAllByc method should be in that that findById method can assume that id is unique and perform some optimisations, simplifications or whatever it does based on that information, but findAllByc can not since when searching by some column in the table, you can not assume that that column is unique. Even one more reason why Spring should not assume that that column is unique is because I added "All" to the method name, which indicates to the Spring Boot that I expect this method to return a list which may contain more than one item. So how could Spring assume that c is unique inside this method when it returnes a list of rows. So if row is unique, then how could there be multiple rows with the same id? It simply does not make any sense to assume this.
Since I did not implement this method, but merely declared it, and let the Spring implement it, this, I would say, is a bug in the Spring Boot.
In any case, I went to investigate how Spring Boot parses this method name and converts it into query, and it turned out that it does it correctly! Just like one would expect it to parse this method name.
I went on the server log and looked up what query is sent to the server by my program when findAllByc("a"); is called and this is the result written in the server log at the time of program execution:
select t0_.c as c1_0_, t0_.c2 as c2_0_ from s.t t0_ where t0_.c=$1
parameters: $1 = 'a'
As you can see, Spring Boot constructed the correct query from the method name, but still managed to mess up the results.
Of course, if I directly execute this query using plain Java Data Base Connectivity Application Programming Interface (so by bypassing Spring and Hibernate), the returned result is as expected:
a|a1
a|a2
a|a3
So why does Spring Boot messes up results despite that correct query is used, and correct table snippet really is returned from the server? Why does it duplicate rows?
Sure, quick fix is to mark both c and c2 columns with Id annotation in my t class, to explicitely tell Spring that c can not be assumed to be unique, since it is only part of the id, but, first of all, why would I have to do that. It is clear from the method name what I want, and correct Structured Query Language query is the proof that Spring Boot understood me perfectly well.
And secondly, marking both columns as id would work just fine if both as and bs can occur zero to many times in the c column. But, in this case as can occur zero to many times, but a b must occur once and only once in the column. So, if I would to mark both c and c2 as id, I would get that Spring Boot treats a b in the c column as being just one of multiple bs. It would allow multiple bs to be inserted into the table.
For example, if I mark both columns as being ids, and then try to update c2 value of a b in c column by using the save method, Spring would simply think of this entity as being another entity and would simply add another b into the table, which should not happen.
So, I want entities with a in the c column to be inserted into the table when save method is called and the one with b in the c column to be updated when this method is called. Since save is not splited into insert and update methods, I can not tell Spring explicitely whether I want it to perform an update or insert operation. So that is why I can not set both columns as id. I mean save does not work now as it is because it only updates the first row with a in the c column instead of inserting it, but that is another problem. I will override the save method in tr repository, so this is not important for this question. Focus in this question is in findAllByc method.
Sure, the best solution is to add id column in the t table and give it some random number to make Spring happy and then move the Id annotation to that field in t class, sure, but why would I be required to do so when findAllByc is recognized correctly and correct query is produced. Spring simply messes up when extracting the data returned from the server. Beside, this only generates useless data in the database table where I simply do not need ids. If I were to add id column, I would never use it. Is it really worh wasting data storage just to make Spring happy?
Is this a bug?
Can I fix it on some other way without adding unique id column to the table?
I need to get different java Pojo result mapping from same query with different column in sql "Where" condition.
Es.
#Select("Select field1,fiel2,...,fieldn From table where #{column}=1")
List<Pojo> getGenericDetails(#Param(column));
Mybatis "discriminator" doesn't make the job.
Can anyone know how to do that?
Thanks in advance
The Mapping what you have written works But it works on same POJO .
If you need different POJOs based on the column in where clause then you Have to write different queries for each of condition in where clause .
Which is as good as specifying column name in query .
So i suggest you to write different queryies and created Different POJOs and make the method name relevant to context where you will use those queryies
If want to get different java Pojo result mapping from the same query with different column in SQL "Where" condition, I will try the following:
#Select("Select field1,fiel2,...,fieldn From table where #{column}=1")
List<Map<String,Object>> getGenericDetails(#Param(column));
as a result, the key in the map is the field1,fiel2,...,fieldn the value will be the result of field1,fiel2,...,fieldn.
Hope the above helps.
Following documentation of spring-data that is available here there is possibility of creating custom queries using naming convention of methods.
I can find record by some value in column1 using findByColumn1 and find objects that contains NULL in column2 by using findByColumn2IsNull, but when I try to do something like
MyResult result = findByColumn1AndColumn2IsNull("expectedValueOfColumn1");
it returns results that contains column2 property that is set to non-null value.
Can somebody tell me if I'm using the query method wrong, or there is some other issue here?
It seems that JOOQ is completely ignoring the default values of database columns. Neither gets the ActiveRecord object updated nor does it skip this column on INSERT. Instead it tries to set it to NULL which fails on NOT NULL columns.
Example:
CREATE TABLE bug (
foo int,
bar int not null default 42
);
BugRecord b = jooq.newRecord(BUG);
b.setFoo(3);
b.store();
assertNotNull(b.getBar()); // fails
Record r = jooq.select().from(BUG).fetchOne();
assertEquals(new Integer(-1), r.getValue(BUG.BAR)); // fails
// DataMapper pattern
Bug b = new Bug();
b.setFoo(3);
bugDao.insert(b); // Fails because it tries to set "bar" to NULL
The behaviour I would expect is that either the newRecord() initializes all default variables with the korrekt values (although I understand that this could be difficult if the result is the outcome of a custom function :-)).or that the INSERT INTO does not insert all unmodified columns with default values and then that the INSERT INTO is followed by a SELECT that fetches the now existing values from the database (similar to a RETURNING).
Is this really a bug/limitation or am I missing some config option etc which makes it
possible to use "not null default" columns?
You've spotted a couple of things here (all relevant to jOOQ 3.1 and previous versions):
Returning default values from inserts:
BugRecord b = jooq.newRecord(BUG);
b.setFoo(3);
b.store();
assertNotNull(b.getBar()); // fails
That would be a nice-to-have feature, indeed. Currently, jOOQ only fetches IDENTITY column values. You can use the INSERT .. RETURNING syntax or the UPDATE .. RETURNING syntax to explicitly chose which columns ought to be returned after an insert or update. But being able to do so in regular CRUD operations would be much better.
This had also been mentioned in this thread. The relevant feature request for this is #1859.
You can work around this issue by calling
b.refresh(); // Refresh all columns
b.refresh(BUG.BAR, ...); // Refresh only some columns
Inserting NULL vs. inserting DEFAULTs through UpdatableRecord:
Record r = jooq.select().from(BUG).fetchOne();
assertEquals(new Integer(-1), r.getValue(BUG.BAR)); // fails
This is a bug, in my opinion. jOOQ's CRUD operations should be DEFAULT value safe. Only those values that have been set explicitly prior to a store() / insert() / update() operation ought to be rendered in the generated SQL. I have registered #2698 for this.
Inserting NULL vs. inserting DEFAULTs through DAO:
// DataMapper pattern
Bug b = new Bug();
b.setFoo(3);
bugDao.insert(b); // Fails because it tries to set "bar" to NULL
Nice catch. This is non-trivial to solve / enhance, as a POJO does not ship with an internal "changed" / "dirty" flag per column. It is thus not possible to know the meaning of a null reference in a POJO.
On the other hand, jOOQ already knows whether a column is nullable. If jOOQ also maintained metadata about the presence of a DEFAULT clause on a column, it could deduce that the combination NOT NULL DEFAULT would have to lead to:
INSERT INTO bug(foo, bar)
VALUES(3, DEFAULT)
And to
UPDATE bug SET bar = DEFAULT WHERE foo = 3
I have registered
#2699: Adding some metadata information to generated code
#2700: Leveraging the above metadata in SQL from DAOs
I have a table (Oracle database) that looks like:
CREATE TABLE example
(
idEx INTEGER,
idAdh INTEGER,
date DATE,
PRIMARY KEY (idEx, idAdh)
)
I have generated the corresponding classes in Netbeans and I have two classes created for this table: Example.java and ExampleId.java, this last one containing the two values of my primary key.
Now let's say I have some records here and I would like to delete one using only one value of the primary key (idEx for example, which is unique too). So first I need to get that row, but I can't find a way to do this. Would it be possible to do something like this?
Example ex = (Example) session.get(Example.class, new ExampleId(?, idEx));
I'd need something to replace that ? that would act as a wildcard.
Or maybe this is absolutely not the way to go and in this case I'd really appreciate some advices.
You should be able to get the ex with a HQL query.
There is no way to get this without an HQL/JPQL/Criteria query, for good reason.
The get method returns a single object and automatically generates a query like select * from table where key = :key.
The only way you can guarantee that this query returns exactly one row is when you specify the entire key. If you don't have the full PK object available, the query will return a list of objects, at which point get is not appropriate anymore.