Closed almothafar closed 4 years ago
Ops! I just noticed it might be SQL driver issue com.microsoft.sqlserver.jdbc.SQLServerException: Invalid column name 'locationId'.
, isn't? or an issue with generating script to SQL server, not checked this while debugging.
I'm not really sure who is responsible...😅
Is it starting from here?:
at io.ebeaninternal.server.query.CQuery.prepareResultSet(CQuery.java:377)
at io.ebeaninternal.server.query.CQuery.prepareBindExecuteQueryWithOption(CQuery.java:325)
at io.ebeaninternal.server.query.CQuery.prepareBindExecuteQuery(CQuery.java:320)
at io.ebeaninternal.server.query.CQueryEngine.findMany(CQueryEngine.java:384)
Sorry for lack of knowledge around this part, I'm learning :)
Do you know if this SQL Server instance has case sensitive collation for the dictionary?
So column name LocationId works but locationId doesn't?
There are a lot of people that tend to use quoted identifiers everywhere with sql server. So instead the column would be [LocationId]
.
With most other databases people tend to use underscore naming convention (and that is the default for Ebean) so location_id
... and the database dictionary is case insensitive so LOCATION_ID
is also fine to use.
Are these column names and table names already defined in the application? Are people using quoted identifiers everywhere (for all table names and column names) with this database?
For SQL Server, it is not case sensitive, if I got what you mean correctly, so if I select LocationId
, or LOCATIONID
, or LoCaAtIoNiD
all works fine.
And for the column name, I understand that Ebean taking underscore as default convention, but I can override that by specifying the name with @Column
, that works fine and map columns fine.
My problem is as described in the original post, I'm not really sure if I'm getting what you mean, but another example here:
@Entity
@Sql
public class FilePathExtractedData extends Model {
@Id
@Column(name = "ID")
private Integer id;
@Column(name = "WOID")
private int woid;
@Column(name = "ReportPath")
private String reportPath;
}
final String sql = "SELECT id, WOID, reportPath FROM fnGetReportDocsForOrder(:order)";
return supplyAsync(() -> {
final RawSql rawSql = RawSqlBuilder.parse(sql).create();
return Ebean.find(FilePathExtractedData.class)
.setRawSql(rawSql)
.setParameter("order", order)
.findList()
.stream();
}
, ec);
The code above is working fine, the fun part that if I changed WOID
to woid
in String sql the code still works,
final String sql = "SELECT id, woid, reportPath FROM fnGetReportDocsForOrder(:order)";
if I changed it to:
final String sql = "SELECT id, wOID, reportPath FROM fnGetReportDocsForOrder(:order)";
I get exception Property [wOID] not found on models.FilePathExtractedData
which is fine.
BUT if I changed property name to:
@Column(name = "WOID")
private int WOID;
There is no way this code going to work, whatever I provide in String sql WOID or woid, both are not going to work with error: Property [woid] not found on models.FilePathExtractedData
Something messed up here, but I'm not sure what SQL Server is going to do here, as I have a full project working well in production, but I have a few areas that I need to do a workaround for them, as LocID
column should have locId
as property otherwise it is not going to work.
What I understand is, String sql should be providing the property name and @Column
should be respected to deal with mapping with the real physical column in SQL server.
BTW, The statement in native sql tool is working fine, I test them first to see my results, then translate them to java or ebean version of sql, also, everything is working fine with hibernate, and working fine using Ebean.createSqlQuery(sql)
instead.
Hmmm.
@Id
@Column(name = "Id")
private Integer id;
@Column(name = "ZipCode")
private String zipCode;
@Column(name = "LocID")
private Long locationId;
@Column(name = "CompanyName")
private String companyName;
The problem with the above with the database case insensitive dictionary (most common) is that the above column names don't follow the the default UnderscoreNamingConvention
.. and strictly speaking does not follow the MatchingNamingConvention
... it doesn't follow any Ebean provided naming convention.
The goal
is actually to have a naming convention and follow it such that we don't need to use @Column
at all (and similarly we don't need to use @JoinColumn
etc). This should be the goal / approach for any new database schema.
We ought to only use @Column
and @JoinColumn
when we are required to support and existing database schema.
I'm not sure where you are at with this. If this is a new application / database schema I recommend that you remove all @Column
and @JoinColumn
annotations (which then means everything will follow the naming convention chosen).
Where are you at with this issue?
@rbygrave I'm with a new application with very old Database system like a few years ago, running on MS Server 2012, what I understand the naming convention of ebean, and I follow it when I own the database and the application.
But I started with a new project using JPA starter that was using Hibernate, I decided then to see Ebean and found that it is better for performance and even a lot of less code, actually I'm Ebean fan from 3 years ago, but this is the first time I work with old exist SQL server, and almost the first time I work with database that I don't own, I just got a user to read/write but for dev I got my own database (SQL Server 2017) that I try to make it like prod one.
This is the story, about the issue, what I understand is ebean respecting @Column
on everything except this case, for some reason, if I try to query using: Ebean.find(LocationData.class).where().eq("locationId", something).findList()
as for example, the code is working fine, and yes with @Column(name = "LocID")
.
Sadly, I got a lot of areas that I need to provide @JoinColumn
and @Column
, as I said, I don't own the DB, and it got a lot of real data, like millions of rows, actually my application and DB is already live in production.
Ok. But when we see @Column(name = "LocID")
... this can also be stated as @Column(name = "LOCID")
... as the DB dictionary isn't case sensitive right. (So LocID is somewhat misleading and that can be problematic for resultSet meta data based features (findNative and findDto).
Can you re-state the minimum failing test again?
I think we need to start again fresh. Thanks.
Ok, I'll close this for now. You can re-open re-stating the issue again. Thanks.
@rbygrave
The problem does not relate to letter case, as for example, I got the same issue in different property:
@Column(name = "Received")
private LocalDateTime receivedDate;
When my String sql = "SELECT Received from someFunction()"
I get error: Property [Received] not found on models.SomeModal
and when my String sql = "SELECT receivedDate from someFunction()"
i get error: Invalid column name 'receivedDate'.
I tried:
@Column(name = "RECEIVED")
private LocalDateTime receivedDate;
Still same errors.
Also,
If I get something like:
@Column(name = "STID")
private int STID;
And selecting STID
with String sql = "SELECT STID from someFunction()"
, I get error: Property [stid] not found
For LocID
I tried:
@Column(name = "LOCID")
private int locationId;
Still having the same issues, Invalid column name 'locationId'.
and Property [locid] not found
Can you look to provide a project with a failing test. Thanks.
On Fri, 13 Sep 2019 at 02:46, Al-Mothafar Al-Hasan notifications@github.com wrote:
Also,
If I get something like:
@Column(name = "STID") private int STID;
And selecting STID with String sql = "SELECT STID from someFunction()", I get error: Property [stid] not found
For LocID I tried:
@Column(name = "LOCID") private int locationId;
Still having the same issues, Invalid column name 'locationId'. and Property [locid] not found
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/ebean-orm/ebean/issues/1780?email_source=notifications&email_token=AABTATMULKCPW7NFBC6QLS3QJJI4JA5CNFSM4IIOJ472YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD6SER4Q#issuecomment-530860274, or mute the thread https://github.com/notifications/unsubscribe-auth/AABTATMQYG2DZN3V2GMECYLQJJI4JANCNFSM4IIOJ47Q .
Hey @rbygrave sorry for the delay as I was really busy in a project here, I have created a new project that can reproduce the problem:
https://github.com/almothafar/play-java-ebean-sql-server
I created a silly example but I hope that enough, As you can test that when you start play application, hit http://localhost:9000/location/1?locName=true will reproduce the issue, http://localhost:9000/location/1?locName=false is not, because when you do locName=false
the normal ebean query run:
public CompletionStage<List<Company>> companiesByLocation(long locationId) {
return supplyAsync(() -> ebeanServer.find(Company.class).where()
.eq("locationId", locationId)
.orderBy("name").findList(), executionContext);
}
But with locName=true
, RawSql workign here:
public CompletionStage<List<CompanyExtracted>> companiesByLocationFn(long locationId) {
final String sql = "SELECT id, name, locationName, locationId FROM fnGetCompanyWithLocation (:id) result";
return supplyAsync(() -> {
final RawSql rawSql = RawSqlBuilder.parse(sql).create();
return Ebean.find(CompanyExtracted.class)
.setRawSql(rawSql)
.setParameter("id", locationId)
.findList();
}, executionContext);
}
If you change locationId
to locId
in CompanyExtracted.java
, the API here will work!
As you can see here, the only problem is RawSql while the normal ebean method is OK whatever the property name is.
@rbygrave are you able to test that?
May you reopen this issue to keep track of it?
Thanks.
I haven't looked at it yet - will reopen though.
Right so this isn't a bug with how RawSql is currently designed.
Raw sql is parsed without ANY knowledge of the type of entity being used and can be treated as a thread safe constant (static final).
So in the test case the mapping below is solely based on the naming convention.
final RawSql rawSql = RawSqlBuilder.parse(sql).create();
When things don't map by naming convention then explicit column mapping needs to be used. In this case that would be via:
final RawSql rawSql = RawSqlBuilder.parse(sql)
.columnMapping("LocId","locationId")
.create();
Changing the design to dynamically map based on @Column
means we can lose that thread safe constant nature (static final).
I'll try to do mapping, I think I did that before but didn't work maybe, I don't know, I need to try that again on Sunday.
OK with Mapping it seems working fine now, but I have to specify column name in DB, also, it is case sensitive, which is fine I think, but just a note.
Cool - closing.
I got a strange issue that I'm pretty sure is a bug; let's say I got a column called
LocID
in the database, which is mapped to propertylocationId
of an entity mapped using @Column like@Column(name = "LocID") private Long locationId;
The problem is when I try to make a query using
RawSqlBuilder.parse(sql).create()
I get an issue of exception related to a property name (Property [locId] not found on
) or column name (Invalid column name 'locationId'.
). if I tried to select either ofLocID
orloactionId
, other properties are fine, which make me believe it is a bug, that I renamedlocationId
tolocId
and worked!I tried to rename another field like zipCode to postalCode with
@Column(name = "ZipCode")
got the same issue on this column too, in short, no issues at all if the property name MATCH the column name.I tried
columnMapping
approach got the same issues, same exceptions.Expected behavior
Selecting a property name in a query should be working fine.
Actual behavior
The property name MUST match column name, and can't be selected in any way if they are different.
Steps to reproduce
And query part for
RawSql
Using SQL Server as you may note in exceptions.
First attempt (it does not make sense):
A second attempt (it does make sense):