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;
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 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()
The #Query on the property retrieves the values only if I retrieve the entity from the DB.
#NodeEntity
public class Team
{
#GraphId
private Long nodeId;
#RelatedTo (type = "PREVIOUSLY_KNOWN_AS")
private Team previouslyKnownAs;
#Query ("START t=node({self}) MATCH t-[:PREVIOUSLY_KNOWN_AS]-other RETURN other")
private Iterable<Team> aliases;
}
The below test works only if I uncomment the line to read it explicitly from the db. Why is it necessary? I see the query being run after the save(t) but the alias field is null if I doesn't read it from DB by uncommenting the line
#Test
public void alias()
{
Team t = new Team();
t.setName("Alpharetta One");
Team prev = new Team();
prev.setName("Previous Name");
teamRepo.save(prev);
t.setPreviouslyKnownAs(prev);
teamRepo.save(t);
//t = teamRepo.findOne(t.getNodeId());//only works if I uncomment
assertNotNull(t.getAliases());
}
Try
t=teamRepo.save(t);
I dont think that the save operation will update the POJO which you give to it, while the returned Object should be a managed enttiy.
The key lies in the reference documentation
The #Query annotation leverages the delegation infrastructure supported by Spring Data Neo4j.It provides dynamic fields which, when accessed, return the values selected by the provided query language expression.
Since it is a dynamic field, the value isnt instanciated but instead fetched from the DB every time the get method is called. To do this, a proxy object has to be used. However there is no way for SDN to change your t Object reference to the proxy object, and thats why its not working, if you are not using the entity returned by save().