In Grails Service, I have to delete records from the Db, but I get below error :
spi.SqlExceptionHelper, Connection is read-only. Queries leading to data modification are not allowed.
Although, there is #Transactional(readOnly = false) in my service, here is the delete part in my service code:
def adsDurationIs7 = null
adsDurationIs7 = Ads.findAllByDuration("7 days", [sort: "dateCreated", order: "asc"])
adsDurationIs7.each {
Ads.get(it.id).delete(flush: true)
}
I'm not sure why that's not working, but even if it did you're doing the work about as expensively as you possibly can. You load all of the instances into memory (including all non-lazy properties and collections), then for each one you get its id and use that to load the instance again with a get call (although if you're lucky and/or you have caching configured correctly this might be a no-op), and then use that to delete each database record one at a time. And you're ordering in the database, which adds processing time but is completely unnecessary since you're deleting everything that the query returns.
What you really want is GORM code that ends up running SQL similar to
delete from ads where duration=?
where the PreparedStatement sets the ? parameter value to "7 days".
This "where" query will do exactly that:
Ads.where { duration == '7 days' }.deleteAll()
as will this HQL update:
Ads.executeUpdate 'delete Ads a where a.duration = :duration',
[duration: '7 days']
So your service should look like
import grails.transaction.Transactional
#Transactional
class MyService {
void deleteAdsDurationIs7() {
Ads.where { duration == '7 days' }.deleteAll()
}
}
or
import grails.transaction.Transactional
#Transactional
class MyService {
void deleteAdsDurationIs7() {
Ads.executeUpdate 'delete Ads a where a.duration = :duration',
[duration: '7 days']
}
}
You are executing this service function from controller's function, which is not transactional. Add #Transactional to controller's function.
Here is an example:
https://stackoverflow.com/a/21998182/2166188
Related
I'm using Grails 3.3 and GORM CriteriaBuilder for most of my querying these days but I am stuck on how to call functions on properties - for example, calling the hour() function on a Date property. If I were using where DetachedCriteria I could have something like this:
def whereQuery = Student.where {
hour(registration) = 15
}
to find all students who registered between 15:00 and 15:59.
However, if I use a CriteriaBuilder instead, I cannot do this:
def c = Student.createCriteria()
def results = c.list {
eq 'hour(registration)', 15
}
Is there some way to accomplish this in the builder DSL? I know I can go back to the domain class and define a mapping that extracts the hour from the date field but that seems kind of clumsy.
Give the sqlRestriction a try:
def results = Student.withCriteria {
sqlRestriction 'hour(registration) = 15'
}
See 7.5.6. Using SQL Restrictions in http://gorm.grails.org/latest/hibernate/manual/index.html#criteria
What is the correlation between Spring org.springframework.transaction.annotation.Transactional annotation and Neo4j OGM org.neo4j.ogm.session.Session.getTransaction() method.
I'm trying to access the current transaction via session.getTransaction() inside of the method annotated with Spring #Transactional but always getting null.
I have added a following code inside of my Spring MVC RestController method:
Transaction tx = session.beginTransaction();
try {
for (int i = 0; i < 10; i++) {
initializeNode(node);
}
}
tx.commit();
} catch (Throwable th) {
logger.error("Error while inserting mock data", th);
th.printStackTrace();
} finally {
tx.close();
}
in case of the following method:
private void initializeNode(TestNode node) {
System.out.println(session.getTransaction());
}
it prints current tx - so far everything is okay.
But in case of the following method:
private void initializeNode(TestNode node) {
System.out.println(session.getTransaction());
User admin = userDao.findByUsername("admin");
}
first time it prints current tx and then null... transaction disappear before commit for a some reason..
this is findByUsername method:
#Service
#Transactional
public class UserDaoImpl implements UserDao {
#Override
#Transactional(readOnly = true)
public User findByUsername(String username) {
return userRepository.findByUsername(username);
}
...
}
Right after that on commit I'm getting a following exception:
org.neo4j.ogm.exception.TransactionManagerException: Transaction is not current for this thread
at org.neo4j.ogm.session.transaction.DefaultTransactionManager.commit(DefaultTransactionManager.java:100)
at org.neo4j.ogm.transaction.AbstractTransaction.commit(AbstractTransaction.java:83)
at org.neo4j.ogm.drivers.embedded.transaction.EmbeddedTransaction.commit(EmbeddedTransaction.java:77)
What am I doing wrong ? Why transaction disappears ?
There are several issues and themes going on in this question. I will try and break them down and hopefully at the end it will all make sense.
As of the latest release of Spring Data Neo4j (4.1.x) there is no correlation between Spring's #Transactional and the Neo4j OGM's Session.getTransaction() or Session.beginTransaction() when called directly.
In your first two code blocks you are completely managing your OGM session lifecycle directly. Spring is not involved at all at this point and as you say it executes as expected.
In your updated third code block you are now expecting the session that you have manually opened to work with your Spring managed DAO. What will happen here is depends on the Neo4j Driver you are using with SDN but essentially because your DAO has the #Transactional annotation, Spring will intercept the call and start a brand new transaction all on its own on top of the one you are manually managing. At this point, we can't make any guarantees about the behaviour but the easiest explanation would be to say that it will be unexpected (again, depending on the driver used).
So how can you fix this?
I'm going to assume you want to use Spring Transactions and Spring Data Neo4j. If that's the case you will want to start by:
Changing your DAO to use Spring Data Repositories. This gives you a lot of free persistence functionality like finders, saves, deletes etc.
Putting the #Transactional annotation around the unit of work you want to accomplish. You might have a method that calls userRepository.findByUserName(), modifies that user and calls userRepository.save(user). In a web environment this is typically some sort of service method.
Removing any code that manually starts or ends an OGM session transaction.
You can find a very short code sample here and a longer code sample here.
A more comprehensive guide can also be found here.
In Spring Data Neo4j 4.2.x we hope to introduce some more powerful and friendlier #Transactional behaviour so keep posted for that update.
I'm not a programming savvy person, so please bear with me.
I've read blog entries and docs about command object. I've never used it and was wondering if I should. (I probably should...)
My project requires parsing, sorting, calculating, and saving results into database when users upload files.
So according to one of the blog entries I read and its corresponding github code,
1) SERVICE should receive file uploads, parse uploaded files (mainly docs and pdfs), sort parsed data using RegEx, and calculate data,
2) COMMAND OBJECT should call SERVICE, collect results and send results back to controller, and save results into the database,
3) CONTROLLER should receive request from VIEW, get results from COMMAND OBJECT, and send results back to VIEW.
Did I understand correctly?
Thanks.
I found this to be the best setup. Here is an example that I use on production:
Command Object (to carry data and ensure their validity):
#grails.validation.Validateable
class SearchCommand implements Serializable {
// search query
String s
// page
Integer page
static constraints = {
s nullable: true
page nullable: true
}
}
Controller (directs a request to a Service and then gets a response back from the Service and directs this response to a view):
class SomeController {
//inject service
def someService
def search(SearchCommand cmd) {
def result = someService.search(cmd)
// can access result in .gsp as ${result} or in other forms
render(view: "someView", model: [result: result])
}
}
Service (handles business logic and grabs data from Domain(s)):
class SomeService {
def search(SearchCommand cmd) {
if(cmd.hasErrors()) {
// errors found in cmd.errors
return
}
// do some logic for example calc offset from cmd.page
def result = Stuff.searchAll(cmd.s, offset, max)
return result
}
}
Domain (all database queries are handled here):
class Stuff {
String name
static constraints = {
name nullable: false, blank: false, size: 1..30
}
static searchAll(String searchQuery, int offset, int max) {
return Stuff.executeQuery("select s.name from Stuff s where s.name = :searchQuery ", [searchQuery: searchQuery, offset: offset, max:max])
}
}
Yes, you understood it correctly except the one thing: command object shouldn't save the data to DB - let service to do that. The other advantage of command object is data binding and validation of data from the client. Read more about command objects here grails command object docs
You can also find helpful information regarding your question in this article
grails best practices
I guess not. Its not really related to whether the save is done in a service it should always attempt to carry out complex stuff and specifically db stuff in a service. so that is regardless. I tend to not use command object but have got hooked on helper classes aka beans that sit in src/main/groovy and do all of the validation and formatting. I just did a form and in it has feedback and reason.
Initially I thought I would get away with
def someAction(String feedback, String reason) {
someService.doSomething(feedback,reason)
}
But then I looked closed and my form was firstly a textarea then the selection objects were bytes so above was not working and to simply fix it without adding the complexity to my controller/service I did this:
packe some.package
import grails.validation.Validateable
class SomeBean implements Validateable {
User user
byte reason
String feedback
static constraints = {
user(nullable: true)
reason(nullable:true, inList:UsersRemoved.REASONS)
feedback(nullable:true)
}
void setReason(String t) {
reason=t as byte
}
void setFeedback(String t) {
feedback=t?.trim()
}
}
Now my controller
class SomeController {
def userService
def someService
def doSomething(SomeBean bean){
bean.user = userService.currentUser
if (!bean.validate()) {
flash.message=bean.errors.allErrors.collect{g.message([error : it])}
render view: '/someTemplate', model: [instance: bean,template:'/some/template']
return
}
someService.doSomeThing(bean)
}
}
Now my service
Class SomeService {
def doSomeThing(SomeBean bean) {
if (bean.user=='A') {
.....
}
}
All of that validation would have still had to have been done somewhere, you say no validation but in a good model you should do validation and set things to be stored in proper structures to reduce overloading your db over time. difficult to explain but in short i am talking about your domain class objects and ensuring you are not setting up String something string somethingelse and then not even defining their lenghts etc. be strict and validate
if you have a text area this will be stored in the back end - so you will need to trim it like above - you will need to ensure the input does not exceed the max character of the actual db structure which if not defined will probably be 255
and by doing
static constraints = {
user(nullable: true)
reason(min:1, max:255, nullable:true, inList:UsersRemoved.REASONS)
Has already invalidated it through the bean.validate() in the controller if the user exceeded somehow my front end checks and put in more than 255.
This stuff takes time be patient
Edited to finally add in that example byte - is one to be careful of -
When adding any String or what ever I have started to define the specific like this and in the case of byte if it is a boolean true false - fine if not then define it as a tinyint
static mapping = {
//since there is more than 1 type in this case
reason(sqlType:'tinyint(1)')
feedback(sqlType:'varchar(1000)')
// name(sqlType:'varchar(70)')
}
If you then look at your tables created in the db you should find they have been created as per definition rather than standard 255 varchar which I think is the default for a declared String.
I'm using grails 1.3.7 together with Oracle 11g and trying to manage inner transactions.
I have a bean Person that is passed to a transactional (Propagation.REQUIRED) service method who makes some modification. Then it is passed to another transactional (propagation = Propagation.REQUIRES_NEW) method that makes some other modification and then throws an Exception.
What I expected to see is the rollback of all the modification of the second service but still valid those of the first one. This is the situation:
//outer transaction
class MyService {
def nestedService
#Transactional(propagation = Propagation.REQUIRED)
public void testRequiredWithError(Person person) {
person.name = 'Mark'
try {
nestedService.testRequiresNewWithError(person)
} catch (RuntimeException e) {
println person.age //this prints 15
println e
}
}
}//end MyService
//inner transaction
class NestedService{
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void testRequiresNewWithError(Person person) {
person.age = 15 //expected after Exception will be discarded
throw new RuntimeException("Rollback this transaction!")
}
}
then I run grails console and check on the DB after it ends.
...
def p = Person.get(671)
def myService = ctx.myService
println p.name //'John'...from DB
println p.age //25...from DB
myService .testRequiredWithError(p)
println p.name // 'Mark'....correct
println p.age // 15....UNEXPECTED..
//same result checking on the DB after console ends and transaction flushes
I tried to use Propagation.NESTED after activating it in the bootstrap by transactionManager.setNestedTransactionAllowed(true)and use savepoints like in this post
grails transaction set savepoint
but still got same result.
What am I missing????
Thank you in advance.
I'm a little late to the party but in case you haven't found your answer I think I know why you're having issues with transactions.
I looked at your link to the discussion about your issues with savepoints. According to that discussion you are using MySQL as your datasource. MySQL does not support transactions by default and in order to make it do so, you need to create your tables in a special way. I have provided a link below that explains how you should create your tables in MySQL when you want to use transactions:
http://www.tutorialspoint.com/mysql/mysql-transactions.htm
EDIT: This article suggests setting the type of your table to InnoDB in order to support transactions. Here is an example of what that looks like:
mysql> create table tcount_tbl
-> (
-> tutorial_author varchar(40) NOT NULL,
-> tutorial_count INT
-> ) TYPE=InnoDB;
It might be worth noting that this is not the only type that supports transaction but is the most common. MySQL 5.5 and greater should create tables as type InnoDB automatically.
Hope this helps!
I'd test for p.isAttached() as transaction rollback detaches the domain object from the Hibernate session. Also in the test I'd reload the domain object from database - effectively p = Person.get(671) again to reload data from database.
I think the reason the age property is set to 15 after the test is that after the exception the domain object and the database are out of sync (and the domain object is detached).
For more see: https://weblogs.java.net/blog/blog/davidvc/archive2007/04/jpa_and_rollbac.html
The simplified domain model:
'Txn' (as in Transaction) hasMany 'TxnStatus'. TxnStatus has a dateTime
This is a legacy mapping so I cant change the DB, the mapping on Txn:
static mapping = {
txnStatus column: 'MessageID', ignoreNotFound: true, fetch: 'join'
}
I need to get Txns based on a number of dynamically built criteria, currently using GORM's 'where' query, it works well; BUT I need to also get only the latest txnStatus.
Tried:
def query = Txn.where {
txnStatus { dateTime == max(dateTime) }
}
gives: java.lang.ClassCastException: org.hibernate.criterion.DetachedCriteria cannot be cast to java.util.Date
also tried:
def query = Txn.where {
txnStatus.dateTime == max(txnStatus.dateTime)
}
which gives:
Compilation Error: ...
Cannot use aggregate function max on expressions "txnStatus.dateTime"
At this stage I am thinking of changing to HQL...any help appreciated!
There was a question a couple of days ago very similar to this. It appears that using where queries with a 'max' subquery doesn't work well with ==
The OP was able to get it to work with < and worked around it that way. Looking at the docs on where queries has not helped me figure this one out.
Here is a really wild guess -
Txn.where {
txnStatus {
dateTime == property(dateTime).of { max(dateTime) }
}
}