I have an existing MySQL database and in grails controller i want use pure SQL query instead of GORM (it's too difficult to run the reverse engineering plugin).
I want return json object
So my idea is, in the controller, to execute a sql query and map the result to an object and render that object in json (i have see the grails REST tutorial anyway).
To map the sql query result to an object, can i use the grails domain class or i must create a pure groovy object?
Can i use the command:
grails create-domain-class
or i must create a DTO groovy object (in src/groovy folder)?
You don't need to create a DTO to solve your problem you just need to use an HQL, like this:
def result = Domain.executeQuery("FROM DOMAIN WHERE conditions");
render result as JSON;
I hope this solve your problem.
If you want to pass the native sql then you can also use spring jdbcTemplate.
Configuration:
import org.springframework.jdbc.core.JdbcTemplate
beans = {
..........
jdbcTemplate(JdbcTemplate) {
dataSource = ref('dataSource')
}
}
Controller:
def jdbcTemplate
List actionMethod(){
String query = "your pure native query example select ........from table"
List results = jdbcTemplate.queryForList(queryStr)
return results as JSON
])
}
Create DTO object and map the required columns one by one .I used the DTO approach since my number of columns to be mapped were quite less.
If your result is simple enough, you can use groovy.sql.Sql class and do the querying directly in the controller:
def results = []
Sql db = new Sql( dataSource )
db.eachRow( "select avg( rating ) as rating, main_profile_id as id from profile" ) {
results << [ it.id, it.rating ]
}
db.close()
Related
I am trying to query a domain that looks like this:
class MyDomain {
String something
String somethingElse
Set someStrings
static hasMany = [someStrings: String]
static mapping = {
//... etc.
someStrings: cascade: 'all'
//... etc.
}
}
The domain is in a dependency I didn't write and can't modify.
I would like to find all MyDomains where the Set someStrings contains, say, 'xyz'.
Please show me how, dynamically, with a criteria, or whatever you consider the best practice, I can do this in Grails. My project and the dependency are using Grails 2.44.
In my opinion, using a Collection as a property in a grails Domain is already an anti-pattern, so asking for a "best practice" on top of that is kind of ironic.
FWIW, here's my attempt.
Grails builds a table for your Set of strings, so you can use a classic SQL query, and bind the results to the domain class, like this:
import myapp.MyDomain
class MyDomainService {
List<MyDomain> findByKeyword(String keyword) {
MyDomain.withSession {session ->
def query = session.createSQLQuery("select distinct main.* from MY_DOMAIN as main inner join MY_DOMAIN_SOME_STRINGS as detail on main.id=detail.MY_DOMAIN_ID where detail.SOME_STRINGS_STRING = :keyword")
query.addEntity(MyDomain.class)
query.setString("keyword", keyword)
query.list()
}
}
}
I could not test the code, so there may be typos. I believe that my table and column names match what grails would generate for your example. In any case the principle of binding a domain class to a resultset works in my code.
UPDATE:
Not sure if it will work, but you could try this:
MyDomain.findAll {
someStrings.contains "xyz"
}
It is theoretically possible within the DSL of where queries, but I haven't tried it. I'd be impressed if they thought about this.
Can anyone tell me how to call stored procedure from Grails which is present in User-defined path.
For that purpose you can use 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.
Note: dataSource and sessionFactory are beans that you would have to inject in your pojo/bean class.
So to execute sql code written on your file:
String sqlFilePath = grailsApplication.parentContext.servletContext.getRealPath("/data/proc.sql")
String sqlString = new File(sqlFilePath).text
Sql sql = new Sql(sessionFactory.currentSession.connection())
sql.execute(sqlString)
This will execute any sql statements written in your file on your sql server.
From JDriven's blog post Grails Goodness: Using Hibernate Native SQL Queries:
In the following sample we create a new Grails service and use a
Hibernate native SQL query to execute a selectable stored procedure
with the name organisation_breadcrumbs. This stored procedure takes
one argument startId and will return a list of results with an id,
name and level column.
class OrganisationService {
// Auto inject SessionFactory we can use
// to get the current Hibernate session.
def sessionFactory
List breadcrumbs(final Long startOrganisationId) {
// Get the current Hiberante session.
final session = sessionFactory.currentSession
// Query string with :startId as parameter placeholder.
final String query = 'select id, name, level from organisation_breadcrumbs(:startId) order by level desc'
// Create native SQL query.
final sqlQuery = session.createSQLQuery(query)
// Use Groovy with() method to invoke multiple methods
// on the sqlQuery object.
final results = sqlQuery.with {
// Set domain class as entity.
// Properties in domain class id, name, level will
// be automatically filled.
addEntity(Organisation)
// Set value for parameter startId.
setLong('startId', startOrganisationId)
// Get all results.
list()
}
results
}
}
In the sample code we use the addEntity() method to map the query
results to the domain class Organisation. To transform the results
from a query to other objects we can use the setResultTransformer()
method.
Hibernate (and therefore Grails if we use the Hibernate plugin)
already has a set of transformers we can use. For example, with the
org.hibernate.transform.AliasToEntityMapResultTransformer (in place of addEntity) each result row is transformed into a Map where the column aliases are the keys of the map.
resultTransformer = AliasToEntityMapResultTransformer.INSTANCE
In my application, the query is being built by appending the first part(where clause) with the second part(order by) using a separate script like QueryBuilder.groovy and hence the order by part is prone to HQL injection which can't be sanitized by using Named Parameters. Therefore, I want to use findAll to retrieve a set of records by passing it a query and sorting and paging parameters separately. I saw an implementation like this:
domainClass.findAll(query,[namedParams],[max: 10, offset: 5])
When i passed sortColumn and sortDirection as named parameters, sortColumn worked fine but sortDirection didn't work. i need a way to either make sortDirection work as a named parameter or any other way which will combine 'sorting by direction' with the findAll result. Many people have suggested on various forums to just use the parameters directly as part of the query but it is unacceptable for my application as it will expose the query to HQL Injection.
Thanks in advance.
here is an example:
queryString = "FROM BookCatalog b WHERE b.bookNumber = :bookNumber"
this is passed to the QueryBuilder.groovy where something like this happens:
sort = "$params.sortColumn $params.sortDirection"
queryString.order(sort)
public void sort(String query){
this.query = this.query+" order by "+query
}
finally findAll retrieves the list of records:
def list = findAll(queryString,namedParams,queryParams)
so as the logic just appends the sorting parameters to the query string a potential hacker can do something like this:
bookCatalogView?offset=2&max=5&sortColumn=1,2,3 **or 1=1**
or
bookCatalogView?offset=2&max=5&sortColumn=1,2,3;**select * from whatever**
Don't concat strings, it's bad practice.
If you want to create complex queries, consider using createCriteria()
SomeDomainClass.createCriteria().list {
order("propName", "desc")
}
or, if you need more control, in the sessionFactory way:
query = sessionFactory.getCurrentSession().createCriteria(DomainClass.class)
query.addOrder(Order.asc("someField"))
query.addOrder(Order.desc("someotherField"))
I would like to use a DTOs when I'm working with grails. My DTO basically looks like this:
class ProjectDTO {
Long id
Long version
String name
}
I've put the file in src/groovy so grails understands this isn't a domain model.
I also have a converter to transfrom my projectDTO into a project object.
#Transactional
def save(ProjectDTO projectInstance) {
if (projectInstance == null) {
render status: NOT_FOUND
return
}
ProjectDTOConverter converter = new ProjectDTOConverter()
Project p = converter.convertFromDTOForSave(projectInstance)
p = projectService.save(p)
List<ProjectDTO> r = converter.convertToDTO(p)
respond r, [status: CREATED]
}
This strategy seems to work fine for GET, POST, and DELETE operations, but not for PUT operations. It seems that for put operations, grails always considers my projectInstance variable to be null, even though it is passed in properly.
I did a SQL spool and it seems that when you pass in any object into a PUT operation, grails assumes you're working with a domain object, and issues a SQL statement to try retrieve the object from the database. If the object can't be retrieved, the projectInstance is nulled out for some reason.
Does anyone know if there is a way to instruct Grails that the object i'm passing in for a PUT operation is not a domain model, and grails shouldn't retrieve it from the database?
I have a requirement where in I need to get some 10 different colums from 5 different tables of oracle. Basically I want to execute a custom select query and I want to understand from you guys whether I can create a custom domain object and get data populated in this domain object using grails. Some thoughts some links where I can explore more, it would be great.
Thanks
You can use straight Groovy SQL with the dataSource bean. This is an example from an integration test.
def dataSource
#Test
public void sql() {
Sql sql = new Sql(dataSource)
List<GroovyRowResult> results = sql.rows("select * from user")
println results[0].first_name
println results[0].<COLUMN_NAME>
}
Whatever raw SQL you need to use can be put inside of the call to sql.rows() and the result is a List of GroovyRowResult which can essentially be treated like a Map
You can try something like suggested here:
Mapping result of a native SQL query to Grails domain class
import com.acme.domain.*
def sessionFactory
sessionFactory = ctx.sessionFactory // this only necessary if your are working with the Grails console/shell
def session = sessionFactory.currentSession
def query = session.createSQLQuery("select f.* from Foo where f.id = :filter)) order by f.name");
query.addEntity(com.acme.domain.Foo.class); // this defines the result type of the query
query.setInteger("filter", 88);
query.list()*.name;