I'm new to Grails and I'm stuck with this problem for hours. Thanks in advance for you help!
Here is my question:
I have a database with two tables
PROJECT
LIKES
As you can guess a user has the right to Like a Project.
In the Domain Class Project there is NO relations (belongsTO, hasOne, etc..)
Same in the Domain Class Likes
In the database the table LIKES has a field project_id but it is not set as a foreign_key. It is this made this way for right purpose.
Now I need to execute a native SQL query with grails which is really simple and returns the result expected.
The result is all projects that have Likes or not
Here is the query :
SELECT project.name, Likes.likes
FROM project
LEFT JOIN Likes
ON project.id = likes.project_id;
I cant find a way to convert this SQL query to HQL.
It seems that HQL works on Domain instance and the fact that there is no relation between the domains return an error like "the Domain Project has no Likes attribute" which is correct.
Is there a way to get the right result with one query or do I need to do two query and build an Array with the result programmatically ?
Thanks for you help
If your domain classes don't have relationships you cannot do it using HQL, as you noticed.
But in Grails you can access the database directly, using groovy.sql.Sql. Example of service:
class MyService {
def dataSource
void addNewRecord(String data) {
groovy.sql.Sql sql = new Sql(dataSource)
sql.execute("insert into my_table(my_anydata_clomn) values(sys.anyData.convertVarchar2(?))",[data])
}
}
Ok it works like this :)
def dataSource
def getProjectList() {
groovy.sql.Sql sql = new groovy.sql.Sql(dataSource)
log.info(sql)
log.info("datasource " + dataSource)
def t = sql.rows("SELECT * \n" +
"FROM project\n" +
"LEFT JOIN Likes\n" +
"ON project.id=likes.project_id\n" +
"ORDER BY likes.likes")
sql.close()
return t;
}
Related
I have SpringBoot webapp using JPA and I have a model class like this:
#Entity
class Server {
.....
private Date updateDate;
}
now I would like to create a custom query inside my repository to get the Server entity with the attribute updateDate nearest to present Date in Oracle 11g database.
Right now I found just a few example, like this for SQLServer:
SELECT TOP 1 *
FROM x
WHERE x.date < #CurrentDate
ORDER BY x.date DESC
I would like something similar to build a custom query with Jpa in Oracle 11g DB.
Thank you all
As you are using Spring Data JPA you can create a repository method:
Server findFirstByUpdateDateLessThan(Date currentDate);
You have to pass the currentDate as parameter because there is no way to use current_date in a repository method.
If you want to use a query that would be possible.
You could also use plain JPA:
List<Server> list = entityManager
.createQuery("select s from Server s where s.updateDate < current_date", Server.class)
.setMaxResults(1)
.getResultList();
If you are sure that you will get one result you could also call getSingleResult()
no, maybe I expressed myself badly. In any case, I want a method, using Jpa, that gives me back the entity whose date is more recent.
The solution is:
public interface ZabbixInstanceRepository extends JpaRepository<ZabbixInstance,String> {
#Query("select z from ZabbixInstance z where z.refreshDate = (select max(z.refreshDate) from ZabbixInstance z)")
ZabbixInstance findByClosestDate();
}
It works perfectly. I hope it helps others.
Thanks anyway
Imagine I have something like this:
def example = {
def temp = ConferenceUser.findAllByUser(User.get(session.user))
[temp: temp]
}
Explaining my problem:
Although dynamic finders are very easy to use and fast to learn, I must replace dynamic finders of my website for sql queries because it is a requirement. As I don't understand SQL that much, my main questions are:
a) I am using an SQLS database, with the drivers and datasource good configured and my website works as it is right now. If I want to replace the "findAllByUser" for an sql statement, should i do something like this:
def dataSource
...
def db = new Sql(dataSource)
def temp = db.rows("SELECT ... ")
b) And that will work? I mean, the temp object will be a list as it is if I use "findAllByUser", and do I need to open a connection to the database =?
With Grails you can use Dynamic Finders, Criteria Builders, Hibernate Query Language (HQL), or Groovy SQL.
To use Groovy SQL:
import groovy.sql.Sql
Request a reference to the datasource with def dataSource or def sessionFactory for transactions
Create an Sql object using def sql = new Sql(dataSource) or def sql = new Sql(sessionFactory.currentSession.connection())
Use Groovy SQL as required
Grails will manage the connection to the datasource automatically.
Sql.rows returns a list that can be passed to your view.
For example:
import groovy.sql.Sql
class MyController {
def dataSource
def example = {
def sql = new Sql(dataSource)
[ temp: sql.rows("SELECT . . .") ]
}
}
And within a transaction:
import groovy.sql.Sql
class MyController {
def sessionFactory
def example = {
def sql = new Sql(sessionFactory.currentSession.connection())
[ temp: sql.rows("SELECT . . .") ]
}
}
I recommend the book Grails Persistence with GORM and GSQL for a lot of great tips and techniques.
yes, with grails you can do both plain sql and hql queries. HQL is 'hibernate query language' and allows you to write sql-like statements, but use your domain classes and properties instead of the table names and column names. To do an hql query, do something like
def UserList = ConferenceUser.executeQuery('from ConferenceUser cu where cu.user = ?', [user]),
what you have here is a parameterized query -- executeQuery sees the ? in the hql string and substitutes the arguments in the array that is the second parameter to the method([user] in this case) for you.
See
http://grails.org/doc/latest/ref/Domain%20Classes/executeQuery.html
and you can see this on how to do sql queries with Grails
Sql query for insert in grails
Going Further / Tips
Use Spring beans
You can make the groovy.sql.Sql instance a Spring bean in your Grails application. In grails-app/conf/spring/resources.groovy define the Sql bean:
// File: grails-app/conf/spring/resources.groovy
beans = {
// Create Spring bean for Groovy SQL.
// groovySql is the name of the bean and can be used
// for injection.
sql(groovy.sql.Sql, ref('dataSource'))
}
Next inject the Sql instance in your your class.
package com.example
import groovy.sql.GroovyRowResult
class CarService {
// Reference to sql defined in resources.groovy.
def sql
List<GroovyRowResult> allCars(final String searchQuery) {
final String searchString = "%${searchQuery.toUpperCase()}%"
final String query = '''\
select id, make, model
from car
where ...
'''
// Use groovySql bean to execute the query.
final results = sql.rows(query, search: searchString)
results
}
}
Multiple Datasources
adminSql(groovy.sql.Sql, ref("dataSource_admin"))
userSql(groovy.sql.Sql, ref("dataSource_user"))
and inject the beans
def userSql
def adminSql
Into the services that need them.
or without injection
import groovy.sql.Sql
// ...
// inject the datasource bean
def dataSource_admin
// ...
// in a method
Sql sql = new Sql(dataSource_admin)
Early Grails Version
Looping through GORM result sets in early grails versions can cause needless queries in the middle of template loops. Using groovy SQL can help with this.
I have a many-to-many relationship between Project and Site. I am trying to retrieve a list of Sites for a project using the Criteria API. I've got this working but the query also selects all of the columns for the associated Projects, which I don't want. I wrote what I thought was an equivalent query using HQL and it only selects the Site columns.
var target1 = session.CreateQuery("select s from Site s join s.Projects pr where pr.ProjectId = ?")
.SetInt32(0, projectId)
.List<Site>();
var target2 = session.CreateCriteria<Site>()
.CreateAlias("Projects", "pr")
.Add(Restrictions.Eq("pr.ProjectId", projectId))
.List<Site>();
How can I limit the Criteria API version (target2) to select only the Site columns? I tried using Projections but there's no method to project a type. I have to use Criteria API in this case.
I'm not sure if this is the best way, but I got it to work using SqlProjection:
Session.CreateCriteria<T>("foo")
.CreateAlias("foo.Bar", "bar")
.SetProjection(Projections.SqlProjection("{alias}.*", new string[] {}, new IType[] {}))
.SetResultTransformer(new AliasToBeanResultTransformer(typeof(Foo)))
.List<Foo>();
This produces the following SQL:
SELECT this_.* FROM Foo this_ inner join Bar b1_ on this_.BarId=b1_.Id
I hope someone can help with this please.
I am trying to query an OLAP Fact table with NHibernate, but am struggling to get it to work. Its seems a simple requirement but I just cant see what the problem could be.
I have a central Fact table with several Dimension tables, one of the Dimensions has a secondary Dimension.
So ERD is. Fact >---1 Factor_Dim >---1 Target_Dim
My NHibernate query is.
facts = session.CreateCriteria(typeof(Fact), "facts")
.CreateAlias("facts.FactorDimension", "factDim", JoinType.InnerJoin)
.CreateAlias("factDim.TargetDimension", "targetDim",JoinType.InnerJoin)
.Add(Restrictions.Eq("targetDim.TargetID", targetId))
.List();
The error is "The multi-part identifier "targetdim2_.TargetID" could not be bound.". The generated SQL does not have the Factor_DIM or Target_DIM tables in the From clause.
Are there any alternative techniques to get this query to work? Id like to stick to this style as opposed to CreateSQLQuery() if possible.
Please help. Thanks.
Linq or QueryOver will be your cleanest solutions. If you are determined to stay with ICriteria you probably would want to wrap each of your entities with a class with common crud methods, it also makes your code access common, so code corrections are done in one place, not over hundres of files or classes.
Theres plenty of projects at http://nhforge.org/wikis/general/open-source-project-ecosystem.aspx which can help you out. I know NhGen ( http://sourceforge.net/projects/nhgen/ ) creates a CRUD class for each entity based on the NHibernate.Burrows GenericDao class with a few CRUD methods. It takes care of all the aliases and joins so queries become as simple as
IMessageDao messageDao = new MessageDao();
// Get All
IList<IMessage> messageList1 dao.FindAll();
// Find using QueryByExample
IList<IMessage> messageList2 = dao.FindByExample(messageDetails, orderBy)).ToList();
// Find using a simple entity query
IList<IMessage> messageList3 = messageDao.Find( new [] { Restrictions.Le(MessageHelper.Columns.Date, dateLastChecked) } );
// Find using a join and a query on said joined entities
IList<IMessage> messageList4 = messageDao.Find
( new []
{
Restrictions.Le(MessageHelper.Columns.Date, dateLastChecked),
Restrictions.Eq(MessageHelper.Columns.IsActive, true))
}, new[]
{
Restrictions.Eq(CategoryHelper.KeyColumns.Rsn, categoryRsn),
Restrictions.Eq(CategoryHelper.Columns.IsActive, true))
}, new []
{
Restrictions.Eq(ChannelHelper.KeyColumns.Rsn, channelRsn),
Restrictions.Eq(ChannelHelper.Columns.IsActive, true))
}
);
Theres plenty of overrides so you can specify your join type or it naturally assumes inner join.
Im having a problem creating a projection for my nhibernate detachedcriteria object.
I have a class Spa which is linked to table Address.
Address has a field called City which is a string.
public class Spa : IAggregateRoot
{
[BelongsTo("AddressID", Cascade = CascadeEnum.All)]
public Address Address { get; set; }
}
My ultimate goal is to get a distinct list of City names.
If i could get all spas with distinct cities i would be happy too.
All my attempts have been for naught and havent found any helpful posts.
So far i've tried:
DetachedCriteria query = DetachedCriteria.For<Spa>()
.CreateAlias("Address", "A")
query.SetProjection(
Projections.Distinct(Projections.ProjectionList()
.Add(Projections.Alias(Projections.Property("Address"), "A"))));
var Spas = ActiveRecordMediator<Spa>.FindAll(query);
I know the above is not correct, just trying to find somewhere to start.
Any help would be appreciated.
Also any simple projections tutorials would be appreciated, cant seem to find anything straight forward out there.
I also tried, but got cast error, looking into it:
DetachedCriteria query = DetachedCriteria.For<Spa>()
.CreateAlias("Address", "A")
.SetProjection(Projections.Distinct(Projections.Property("A.City")));
It seems to me there are two parts to your question.
1. What should my DetachedCriteria look like?
If you are not performing any other aggregations, GROUP BY should provide the same results as DISTINCT. This is the query I would use:
var query = DetachedCriteria.For<Spa>()
.CreateAlias("Address", "A")
.SetProjection(Projections.GroupProperty("A.City"));
2. How do I execute it with Castle ActiveRecord?
I have never used ActiveRecord, but based on the method signatures, I would expect something like this to work:
var cities = ActiveRecordMediator<string>.FindAll(query);
If you have access to the NHibernate session, you could also execute it this way:
var cities = query.GetExecutableCriteria(session).List<string>();