I have a couple of sql views with composite primary keys that I want to query, and since Hibernate makes it a pain to work with composite keyes, I'm using createSQLQuery. The problem is that this method can only return a List, and I need to refer to the colums by their index.
Any chance I could do something like jdbc and refer to the columns by their sql name instead of their index?
Query query=session.createSQLQuery("your query");
query.setResultTransformer(AliasToEntityMapResultTransformer.INSTANCE);
List<Map<String,Object>> aliasToValueMapList=query.list();
As you can figure out from code, the list contains Map objects representing each row. Each Map object will have column name as key and value as value.
Note: This work for SQLQuery, if your using AliasToEntityMapResultTransformer on hql query without specifying aliases you will get index value as key.
If you are again transforming aliasToValueMapList to your POJO list, I advice you to create your own
ResultTransformer and return your custom object from 'transformTuple' method.
Your question is ambiguous - in the first paragraph you want to refer to columns by index and in the second, by sql name. Since by index is easy, I'll assume by name.
First of all, you can use the doWork method to access the underlying JDBC connection and handle it as you would with pure JDBC:
session.doWork(new Work() {
public void execute(Connection connection) throws SQLException {
connection.prepareStatement(...
}
});
Or, you can use query.getReturnAliases which returns a String[] of the column names. For effciency, I'd probably build a Map of alias to index and then you can do something like result[map.get("column name")].
But really, Hibernate handles composite keys pretty easily when using xml mappings (haven't tried with annotations). It's a little more work up front and there are a few issues with complex relationships (mainly when foreign key names/spans don't match), but once you create the id class and map it, you can stick with HQL/Criteria and get all the benefits of lazy loading, simple joins, dirty checking, etc.
I got the same problem but it solved when i used this
Query query=session.createSQLQuery("your query");
query.setResultTransformer(AliasToEntityMapResultTransformer.INSTANCE);
I get the result with header name but i got a new problem when i create a new column in sql query Select 'DCA5E3' as Shipmentcolor from Employee.class
But in this case i got SHIPMENTCOLOR: "D".
How to get whole value of SHIPMENTCOLOR.
Related
Can jOOQ automatically add an alias prefix for all columns of a table in the select clause?
Can jOOQ also help with the 128 byte/character name length limitation of databases?
The reason for the questions are that SimpleFlatMapper is used for the mapping.
SimpleFlatMapper requires the fetched database column names to map to the model.
Model example (really getter/setter are used):
class Head {
public Integer id;
public List<Position> positions;
...
}
class Position {
public Integer id;
public Integer headId;
...
}
The naming can be done individually:
ResultSet resultSet = dsl.select(..., POSITION.ID.as("positions_id"), ...)
.from(HEAD)
.join(POSITION.as("positions")).onKey()
.fetchResultSet();
List<Head> headers = JdbcMapperFactory.newInstance()
.ignorePropertyNotFound()
.newMapper(Head.class)
.stream(resultSet)
.collect(Collectors.toList())
However if it is a complicated model with several joins/columns it is a bit tedious.
The only solution I found was to program a function manually. Have I however maybe overlooked something and jOOQ can do it by itself or help?
Something the likes of this would be very nice (probably not the best naming, I couldn't come up with something better on the spot):
ResultSet resultSet = dsl.select()
.from(HEAD)
.join(POSITION.as("positions").prefixColumns()).onKey()
.fetchResultSet();
Or:
ResultSet resultSet = dsl.selectWithTableAlias()
.from(HEAD)
.join(POSITION.as("positions")).onKey()
.fetchResultSet();
Resulting in the following SQL:
SELECT head.id, head.***, positions.id AS positions_id, positions.headid AS positions_headid, positions.***
FROM head JOIN position AS positions ON head.id = positions.headid
Furthermore databases like Oracle and MSSQL have a limitation of 128 bytes/characters for names.
In very rare complex scenarios the alias names might reach that limit because of the needed nesting.
jOOQ cannot offer a workaround for this in some form or can it? So basically define a name that is used in the SQL and a name for the actual resulting object.
I know, very niche requirements. But they would help a lot with the mapping.
Can jOOQ automatically add an alias prefix for all columns of a table in the select clause?
There's no such feature, because the possible sets of desired auto-prefix algorithms is quite big, and jOOQ ultimately wouldn't do exactly what you want. Maybe, there's room for an SPI to help you do this, in the future. But there isn't one available yet: https://github.com/jOOQ/jOOQ/issues/11545
However, you can easily do this yourself, because every jOOQ query is just a dynamically constructed expression tree, even if you're not always using jOOQ for dynamic SQL.
You can easily write a utility and use that everywhere:
public static List<Field<?>> autoPrefix(Field<?>... fields) {
return autoprefix(Arrays.asList(fields));
}
public static List<Field<?>> autoPrefix(Field<?>... fields) {
Stream.of(fields).map(f -> f.as(myPrefixLogic(f))).collect(toList());
}
This can now be used explicitly on all DSLContext.select() calls, e.g.
dsl.select(autoPrefix(..., POSITION.ID.as("positions_id"), ...))
.from(HEAD)
.join(POSITION.as("positions")).onKey()
.fetchResultSet();
Alternatively, you can wrap your query in a derived table:
public ResultSet fetchResultSetWithPrefix(Select<?> select) {
Table<?> table = select.asTable("t");
return
dsl.select(autoPrefix(table.fields()))
.from(table)
.fetchResultSet();
}
And now, instead of calling fetchResultSet(), you call your auxiliary function:
try (ResultSet rs = fetchResultSetWithPrefix(
dsl.select(..., POSITION.ID.as("positions_id"), ...)
.from(HEAD)
.join(POSITION.as("positions")).onKey()
)) {
...
}
Another, more complex option would be to do this with a VisitListener.
Remember, irrespective of what your query looks like (because jOOQ's DSL mimicks SQL, syntactically), all your jOOQ queries are dynamic SQL queries, so you can relatively easily transform them to whatever you want, automatically.
Can jOOQ also help with the 128 byte/character name length limitation of databases?
I'll have the same reservations about this automatism as before. Where would you see jOOQ provide automatic help here? Given your naming scheme (table_column) would you like a fair distribution of 63 characters per object type? Or is the table less important than the column, and you'll truncate table names at 42 characters, leaving 85 characters for the column?
What if you wanted to fully qualify identifiers, including the schema name, as in schema_table_column? What if you project UDTs, meaning you'll get schema_table_column_attribute1_attribute2
I don't think any automation can be provided by jOOQ which would suit all needs. However, as shown above, it is very simple to implement this only once for your entire application, making sure the naming is always applied correctly. And if an SPI were available, you could implement your abbreviation logic there.
I already used the search here (and other forums as well) but haven't found an answer exacty to what I'm trying to do.
I know that it can easily be done in some other way, and this is just a small sandbox-framework I'm coding for a University course... in a real environment I'd just take Spring, Hibernate etc.
So what I did was coding myself a small generic Data Access Layer with POJOs, working with generic methods to retrieve, check or insert data to the database (Oracle). Most of this is done through PreparedStatements.
This is working as long as I don't have joins... is it possible to put in a Column as parameter?
Example:
Table A has Attribute X + others
Table B has Attribute Y + others
PreparedStatement with query SELECT * FROM A,B WHERE "A"."X" = ?
And then fill in "B"."Y" as the parameter...
The database doesn't throw me an error or exception, but the ResultSet returned after executing the statement is empty. Is it just not possible to do, or am I just missing some escaping?
I'm using PreparedStatement.setString(int index, String value) to fill in the parameter... in lack of ideas which other setX method I could use...
Again, in a real project I'd never code that myself, but rather use something like Spring or Hibernate and not re-invent the wheel, but I see it as an interesting exercise to code such a generic small data access layer myself.
No, JDBC does not allow this. Only column values can be set. If you want to make dynamic changes to the sql statement you will have to do it before you create the PreparedStatement.
I have a JPA entity class (one of many) and I can run JPQL queries on it, returning that entity without any problem. However, when I attempt to run any named native query that selects all the fields on the underlying table, instead of mapping to the entity and returning a list of that entity type, I get a java.util.Vector of object arrays containing the result set. That is, the data is being returned, but not mapped to the entity. This is made worse by Java's fake generics, because the error manifests itself as a NumberFormatException in the EL parser.
My query calling code:
return em.createNamedQuery("ClinicDoctor.findUnchangedByClinicSystemId",
ClinicDoctor.class)
.setParameter(1, clinicSystemId)
.getResultList();
When I switch EclipseLink logging to FINE and run a JPQL query, the column names selected exactly match the column names I'm selecting in the native query.
Am I missing something? Is there some flaming hoop I should be jumping through to get the mapping to work?
Wouldn't you know it, right after asking this question I discovered that I just had to set the result-class attribute on the <named-native-query/> tag in my orm.xml and it worked.
I am working with Java Classes, and I'm looking for the easiest way to connect a DB table in MySQL with the members (attributes) to the Java code.
After executing the "SELECT" query I get a resultSet, yet it is not clear to me how to cast the result Object to the Java Object generically.
For Example: I have 2 Java Classes - Student & Teacher, and I would like to use a command:
Student student = (Student) rs.getObject();
Thanks,
Roi
I would recommend using JPA with eclipselink implementation, rathern than executing queries using plain JDBC, and try to map results from resultsets to objects by hand (error prone). JPA will do that for you !
this doesn't work in that way. You should do something like:
Student st = new Student();
st.setName(rs.getString("NAME"));
st.setAverage(rs.getInt("SCORE"));
where NAME and SCORE are the column names.
The doc can also help you.
You can look at ORM solutions, like Hibernate.
If your question is about rs.getObject.
The call rs.getObject is a generic way of calling rs.getInt, rs.getString because the return values of all these methods are ultimately Objects (Int, String etc...).
It will return the column you ask for (either by order number in the select or by column name). Not the whole bean corresponding to a row.
You use rs.getObject when you do not know at compilation time the type of the returned column.
The JDBC ResultSet Object does not have a magic getObject method that will return the bean corresponding to a select statement.
You can develop your own 'poor man's ORM' with combination of
JBBC ResultSetMetaData
Java Reflexion
But that's a harder work than using an existing one.
I must be really stupid, but I'm at my wits' end with a JPA issue, using MyEclipse 7.5.
I am accessing a DB2 database (on an AS400) via JPA. I have reverse-engineered a simple table to provide a DAO with some precision "find" methods. So far so good.
If I run a SELECT statement over the table thus, I get 4 rows:
SELECT * FROM MyTable WHERE MyValue = '1234'
However, if I try to access these same 4 records via JPA, I get a list that's the right size (4), but which contains 4 objects which are all the same, all copies of the first object found:
List <MyTableObject> objects = dao.findByMyValue("1234");
It's almost as if the internal Query object that the DAO class creates can't iterate through the rows of data. I've tweaked the reveng.xml file myriad ways, and I've tinkered with the generated DAO, but I'm getting nowhere. Am I missing something really obvious here? I just want to get a list of objects in the same way that the conventional SELECT statement returns a resultset!
(This is MyEclipse 7.5, using Hibernate 3.2 and its associated JPA library).
UPDATE: here's the generated code that findByMyValue() passes over to (loggin / try-catch removed for clarity):
#SuppressWarnings("unchecked")
public List<PolicyStatFile> findByProperty(String propertyName, final Object value)
{
final String queryString = "select model from MyTableObject model where model." + propertyName + "= :propertyValue";
Query query = getEntityManager().createQuery(queryString);
query.setParameter("propertyValue", value);
return query.getResultList();
}
FINAL UPDATE
It was all about the model: see comments to this post. Essentially, the model generated from the reverse engineering file was invalid because I didn't have a truly unique key. Once I resolved this (spurred by comments here), all was well.
Method you've posted looks correct (although it seems rather pointless to generate this for all properties). Couple things to check:
Is MyValue property you've mentioned mapped directly on your entity (e.g. to the column on the same table; no associations are involved)?
Can you enable Hibernate SQL debug (set 'hibernate.show_sql' property to true in your configuration) and check what the generated query looks like?
Are 4 objects returned actually the same (e.g. are '==' to each other) or are they copies of each other (e.g. have the same property values)?
Can you post your mapping for the entity in question and generated SQL from #2 above?
I suspect you haven't overridden hashCode() and equals() in your JPA entity (e.g. MyTableObject). So Hibernate can't distinguish the returning rows. That a look here.