Hibernate, SQL server and #SqlResultSetMapping with native query - sql

I'm working with a very old database and I've to retrieve some specific data.
I use sql server and hibernate. I've written a class named Language with a guid (id of a person) and a set known languages. I've to retrieve all languages known by each person in the db, but I don't know if I can fill the languages set with a specific native query.
#NamedNativeQuery(
name = "queryLanguages",
query = "SELECT c.GUIDPersona as guid, d.Caption as language" +
" FROM " +
" Competenze c " +
" INNER JOIN " +
" DomainItemsCaption d " +
" ON (c.IdConoscenza = d.ItemID) " +
" WHERE " +
" d.DomainName = 'Conoscenza' AND " +
" d.Caption LIKE 'Lingue ~ %'",
resultSetMapping = "Languages"
)
#SqlResultSetMapping(
name = "Languages",
entities = {
#EntityResult(
entityClass = Language.class,
fields = {
#FieldResult(name="guid", column="guid"),
#FieldResult(name="languages", column="language")
}
)
}
)
public class Language {
private String guid;
private Set<String> languages;
...
}
Can I fill the "languages" field with a specific native query retrieving all known languages for the person associated to the entity?

Related

How to pass multiple values in a native query and retrieve that data in a Map<String, List<Data>>?

Here I want to pass a list of Ids and date as a parameter to the database query and I want the query to return data in a Map so the key is the id and List is the data against that id.
Query<Data> query = rdsSession.createNativeQuery(DBQueries.DATA_QUERY,
Data.class).setParameterList("performance_id", listOfIds).setParameter("fromdate", fromDate);
List<Data> listDBRecords = null;
Map<String, List<Data> records=listDBRecords.stream().collect(Collectors.groupingBy(Data::getId));
So how can I achieve this result by passing multiple values(ie. ids) in the query and return that data? This is the query I have written
public static final String SELECT_PORTFOLIO_SECURITY_DATA = "select capl.corporate_action_portfolio_level_id, " +
"capl.performance_id, capl.forwardlooking_sequence_no, " +
"capl.as_of_date, capl.price, casl.currency, capl.exchange_rate, " +
"capl.gross_dividend, capl.net_dividend, capl.sub_portfolio_guid, capl.effective_date, capl.adjustment_weighted_factor, capl.price_adjustment_factor, " +
"casl.share_adjustment_factor, casl.float_adjustment_factor, capl.index_shares, capl.input_tos_live, casl.input_float_live, capl.prediction_type from " +
ConfigurationManager.getProperties().getProperty("rdsConnection.schema") +
".corporate_action_portfolio_level capl inner join " +
ConfigurationManager.getProperties().getProperty("rdsConnection.schema") +
".corporate_action_security_level casl " +
"on capl.performance_id = casl.performance_id and capl.as_of_date = casl.as_of_date and capl.prediction_type = casl.prediction_type and " +
"capl.forwardlooking_sequence_no = casl.forwardlooking_sequence_no " +
"where capl.performance_id = :performance_id and capl.as_of_date = :as_of_date and capl.prediction_type = 'PREDICTED' or capl.prediction_type = 'LIVE'";
Can someone tell what is the right way?

How to create a dynamic query using collection-valued named parameters?

As the title suggests, i'm currently trying to add parts to the JPQL-query using collection-valued named parameters (:queryLst).
Function call:
List<PanelSet> psetLst = setRepository.getMaxZchnrGroupByLeftEight(p.getCustomerNumber(), p.getDrawingNumber(), queryLst);
queryLst:
// Is used to store values from scanned and convert them into parts of a query
ArrayList<String> queryLst = new ArrayList<>();
for (int i = 0; i < size1; i++) {
scanEdvRev = scanned.get(i).toString();
queryLst.set(i, "and left(a.drawingnumber, 8) != left('" + scanEdvRev + "', 8)");
}
SetRepository:
public interface SetRepository extends CrudRepository<PanelSet, Integer> {
#Query("select distinct max(a.drawingNumber) from PanelSet a "
+ "where a.customerNumber = :customerNumber "
+ "and a.drawingNumber != :drawingNumber (:queryLst) "
+ "group by left(a.drawingNumber, 8)")
List<PanelSet> getMaxZchnrGroupByLeftEight(#Param("customerNumber") String customerNumber,
#Param("drawingNumber") String drawingNumber,
#Param("queryLst") ArrayList<String> queryLst);
}
When i run the project i get the following exception:
org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: ( near line 1, column 159 [select distinct max(a.drawingNumber) from com.asetronics.qis2.model.PanelSet a where a.customerNumber = :customerNumber and a.drawingNumber != :drawingNumber (:queryLst) group by left(a.drawingNumber, 8)]
I'm unsure whether my approach to this problem is the correct way of doing this and whether this exception is caused by a simple syntax error or by my usage of collection-valued named parameters.
I've followed this guide trying to solve the problem.
EDIT: I'm basically trying to add each String from ArrayList<String> queryLst to the parametrized query inside setRepository.
#Query("select distinct max(a.drawingNumber) from PanelSet a "
+ "where a.customerNumber = :customerNumber "
+ "and a.drawingNumber != :drawingNumber (:queryLst) "
+ "group by left(a.drawingNumber, 8)")
If successful, the query behind the function
List<PanelSet> getMaxZchnrGroupByLeftEight(#Param("customerNumber") String customerNumber,
#Param("drawingNumber") String drawingNumber,
#Param("queryLst") ArrayList<String> queryLst);
should look like this:
queryStr = "select distinct max(a.drawingNumber) from PanelSet a "
+ "where a.customerNumber = " + customerNumber + ""
+ "and a.drawingNumber != " + drawingNumber + "";
for (String s : queryLst) {
queryStr = queryStr + s;
}
queryStr = queryStr + " group by left(a.drawingNumber, 8)";
I hope this clarifies what i'm trying to do with queryLst.
It can't be done using your approach of passing in a list of query chunks.
The closest you'll get is by adding every possible condition to the query and provide values for all those conditions in a way that allows conditions to be ignored, typically by providing a null.
You code might look like this:
#Query("select distinct max(a.drawingNumber) from PanelSet a "
+ "where a.customerNumber = :customerNumber "
+ "and a.drawingNumber != :drawingNumber "
+ "and a.myTextColumn = coalesce(:myTextColumn, a.myTextColumn) "
+ "and a.myIntegerColumn = coalesce(:myIntegerColumn, a.myIntegerColumn) "
// etc for all possible runtime conditions
+ "group by left(a.drawingNumber, 8)")
List<PanelSet> getMaxZchnrGroupByLeftEight(
#Param("customerNumber") String customerNumber,
#Param("drawingNumber") String drawingNumber,
#Param("myTextColumn") String myTextColumn,
#Param("myIntegerColumn") Integer myIntegerColumn);
Passing null for myTextColumn or myIntegerColumn will allow that column to be any value (except null).
You'll have to find SQL that works for the type of conditions you have and the data type of the columns involved and whether nulls are allowed.
If passing nulls doesn't work, use a special value, perhaps blank for text columns and some "impossible" date like 2999-01-01 fir date columns etc and code the condition like:
and (a.myCol = :myCol or :myCol = '2999-01-01')

Joining unrelated tables via JPQL

I have the following query which works. All the tables in this query have relations in some way.
#Repository(value = "ARepository")
public interface ARepository extends JpaRepository<CardEntity, String> {
#Query("SELECT xref.shortUtlTx FROM CardEntity card " +
"JOIN card.apexUrlCrossRef xref " +
"JOIN xref.sampleProdOffer offer " +
"WHERE xref.apexCard.cardNumber = :cardNum " +
"AND offer.apexeeOfferId = :myCode"
)
List<String> getAllValues(#Param("cardNum") String cardNum, #Param("myCode") String myCode);
}
But I also wish to join another table (Entity name -> UrlCountEntity) to this query but that table has no relation to the other tables in this query. Is there a way I could do this?
Based on reading a blog, I tried the following but throws errors.
Added this line to the query:
AND EXISTS (SELECT referCount FROM UrlCountEntity referCount WHERE
referCount.url.urlTx = xref.shortUtlTx)
#Repository(value = "ARepository")
public interface ARepository extends JpaRepository<CardEntity, String> {
#Query("SELECT xref.shortUtlTx FROM CardEntity card " +
"JOIN card.apexUrlCrossRef xref " +
"JOIN xref.sampleProdOffer offer " +
"WHERE xref.apexCard.cardNumber = :cardNum " +
"AND offer.apexeeOfferId = :myCode " +
"AND EXISTS (SELECT referCount FROM UrlCountEntity referCount WHERE referCount.url.urlTx = xref.shortUtlTx)"
)
List<String> getAllValues(#Param("cardNum") String cardNum, #Param("myCode") String myCode);
}
Error as follows:
Method threw
'org.springframework.dao.InvalidDataAccessResourceUsageException'
exception.
could not extract ResultSet; SQL [n/a]
Based on this article:
Using Hibernate 5.1 or newer you can join two unrelated tables via JQPL same way you would do it in SQL:
SELECT first
FROM First first JOIN
Second second ON first.property = second.property
WHERE first.property = :param
So you would need to change your query to something like this:
#Query("SELECT xref.shortUtlTx FROM CardEntity card " +
"JOIN card.apexUrlCrossRef xref " +
"JOIN xref.sampleProdOffer offer " +
"JOIN UrlCountEntity referCount ON referCount.url.urlTx = xref.shortUtlTx" +
"WHERE xref.apexCard.cardNumber = :cardNum " +
"AND offer.apexeeOfferId = :myCode")
List<String> getAllValues(#Param("cardNum") String cardNum, #Param("myCode") String myCode);

JPQL Constructor Expressions, how to eagerly fetch the main entity in 'select new'

The original query I have is somewhat complex, but what I'm trying to do is obtain the entity AlertCondition plus some additional fields.
+ " SELECT new org.rhq.core.domain.alert.composite.AlertConditionEventCategoryComposite " //
+ " ( " //
+ " ac, " //
+ " res.id " //
+ " ) " //
+ " FROM AlertCondition AS ac FETCH ALL PROPERTIES " //
+ " JOIN ac.alertDefinition ad " //
+ " JOIN ad.resource res " //
+ " WHERE " + AlertCondition.RECOVERY_CONDITIONAL_EXPRESSION //
+ " AND ( res.agent.id = :agentId OR :agentId IS NULL ) " //
+ " AND ad.enabled = TRUE " //
+ " AND ad.deleted = FALSE " //
+ " AND ac.category = 'EVENT' " //
+ "ORDER BY ac.id"), //
The problem is Hibernate only selects the ID of AlertCondition, so when accessing this object, this ends up requiring N+1 selects whereas I would like to do only 1.
The select is only fetching the ID column, according to debug:
select alertcondi0_.ID as col_0_0_, alertdefin1_.ID as col_1_0_, resource2_.ID as col_2_0_
What I'm trying to get back are all the fields of *AlertCondition.
I can't find any way to do this under Hibernate. JOIN FETCH doesn't really work here either.
The alternative is to select every column of the table, which I'd like to avoid.
Facing the same issue.
I don't know how to let hibernate fetch ac.* neither.
But I found a workaround.
#Query("select ac, xxx, zzz")
List<Object[]> fetchRecords()
//
Constructor<?> constructor = AlertConditionEventCategoryComposite.class.getDeclaredConstructors()[0];
List<AlertConditionEventCategoryComposite> composites = fetchRecords.stream().map(constructor::newInstance).collect(Collectors.toList());
You can tell hibernate to fetch an ad with:
JOIN FETCH ac.alertDefinition ad

Entity Framework - how to join tables without LINQ and with only string?

I have a question about Entity Framework. Please answer if you know answer on this. I have such query :
String queryRaw =
"SELECT " +
"p.ProductName AS ProductName " +
"FROM ProductEntities.Products AS p " +
"INNER JOIN CategoryEntities.Categories AS c " +
"ON p.CategoryID = c.CategoryID ";
ObjectQuery<DbDataRecord> query = new ObjectQuery<DbDataRecord>(queryRaw, entityContext);
GridView1.DataSource = query;
GridView1.DataBind();
Particularly I want to join few tables in one query, but I can NOT use LINQ and can NOT use ObjectQuery with objects mapped to DB fields inside my query. Because each entity creates dynamically. So this is what i can NOT use :
msdn.microsoft.com/en-us/library/bb425822.aspx#linqtosql_topic12
msdn.microsoft.com/en-us/library/bb896339%28v=VS.90%29.aspx
The question is can I use something like this instead of using objects?
query.Join ("INNER JOIN CategoryEntities.Category ON p.CategoryID = c.CategoryID ");
The purpose is to use Join method of ObjectQuery with syntax as in Where method :
msdn.microsoft.com/en-us/library/bb338811%28v=VS.90%29.aspx
Thanks, Artem
Any decision i see right now is to temporary convert ObjectQuery to string, add joined table as string and then convert it back to ObjectQuery :
RoutesEntities routesModel = new RoutesEntities(entityConnection);
String queryRaw = "SELECT " +
"rs.RouteID AS RouteID, " +
"rs.LocaleID AS LocaleID, " +
"rs.IsSystem AS IsSystem " +
"FROM RoutesEntities.Routes AS rs ";
_queryData = new ObjectQuery<DbDataRecord>(queryRaw, routesModel);
var queryJoin = _queryData.CommandText + " INNER JOIN LocalesEntities.Locales AS ls ON ls.LocaleID = rs.LocaleID ";
_queryData = new ObjectQuery<DbDataRecord>(queryJoin, routesModel);
Maybe someone has more consistent suggestions?
Finally I Found a better solution for this, we can use Sub Query inside main Query. For example :
var db = CustomEntity();
ObjectQuery<Categories> query1 = db.Categories.Where("it.CategoryName='Demo'").Select ("it.CategoryID");
var categorySQL = query1.ToTraceString().Replace("dbo", "CustomEntity"); // E-SQL need this syntax
ObjectQuery<Products> query2 = db.Categories.Where("it.CategoryID = (" + categorySQL + ")");
Some example is here :
http://msdn.microsoft.com/en-us/library/bb896238.aspx
Good luck!