I have this weird problem. I have a User domain class in a Grails app. The class is as follows:
class User {
transient springSecurityService
String username
String name
String password
String email
String company
Date activationDate
String contactPhone
boolean enabled
boolean passwordExpired = false
boolean accountExpired
boolean accountLocked
boolean isDeleted=false
boolean isPrimary
String jobTitle
String jobFunction
String deskPhone
String mobile
String profilePicURL
boolean isLinkExpired=false
UserType userType
Date dateCreated
Date lastUpdated
static constraints = {
password nullable: true
company nullable: true
email blank: false, unique: true
name nullable: true
activationDate nullable:true
username nullable: true
enabled nullable: false
isDeleted nullable: false
passwordExpired nullable: false
jobFunction nullable:true
jobTitle nullable:true
contactPhone nullable:true
mobile nullable:true
profilePicURL nullable:true
deskPhone nullable:true
userType nullable:true
}
static auditable = true
static mapping = {
password column: '`password`'
tablePerHierarchy false
cache true
}
Set<Role> getAuthorities() {
UserRole.findAllByUser(this).collect { it.role } as Set
}
And there is a method activeDeactiveUser which enables/disables user authorization for some functionality as follows:
def activeDeactiveUser(String username) {
def user = User.findByUsername(username)
if (user.enabled == false)
user.enabled = true
else
user.enabled = false
if (user.validate()) {
user.save(flush: true, failOnError: true)
} else {
user.errors.allErrors.each {
print it
}
}
def userJson = new JSONObject()
userJson.put("isEnabled", user.enabled)
return userJson
}
When the app is running on localhost, the table is updating fine. But when the same code is running on server, the table fails to update. I don't know why it's behaving like this.
The app isn't raising any exception on the save method on localhost. May be the problem is with different versions of mysql on my machine and the server. Is there any the to debug the app while it is running on the server?
The app is hosted in an AWS EC2 instance running Ubuntu 14.04 and Grails version 2.4.3. The database is stored in an AWS RDS instance running mysql 5.5.40.
there are many reasons for it - I think you need to provide more information for yourself and in this thread so we can help.
I suggest, first add log information by one of the options:
you can add logSql to dataSource file:
dataSource {
logSql = true
}
to produce far more readable SQL commands than simply logSql would do add the following properties in DataSource.groovy:
hibernate {
format_sql = true
use_sql_comments = true
}
Then, add the following log4j settings to Config.groovy:
log4j = {
debug 'org.hibernate.SQL'
trace 'org.hibernate.type'
}
The first setting logs the SQL commands, the second one logs the bound parameters and the bindings of the result set.
the issue can also be related to schema update - so maybe your local DB schema is not in sync with server one. you ned to check field type and constraints.
Related
I am using Spring Boot to build a scheduled-job data processing application. The main logic would be in a scheduled job that takes a batch of records and process them. I should be running 2 instances of the application that should not pick the same record twice. I tried to utilize the PESSIMISTIC LOCK with NO WAIT to resolve any records selection conflict.
Things are not working as expected. Both instances are picking the same records, although I was expecting only one instance to lock and process a few records and the other instance skip what was locked by the first instance.
Spring Boot version: 2.2.4.RELEASE
Database: MySQl
First I tried using the #Lock and #QueryHint annotations:
#Lock(value = LockModeType.PESSIMISTIC_WRITE) // adds 'FOR UPDATE' statement
#QueryHints(value={#QueryHint(name = "javax.persistence.lock.timeout", value = LockOptions.SKIP_LOCKED+"")})
Page<Transaction> findByStatus(String status, Pageable pageable);
Even with WAIT_FOREVER, there is no change in behavior as if #QueryHints are totally ignored..
The other option I tried is using NativeQuery:
#Query(value ="select * from transaction t where t.status = ?1 limit ?2 for update SKIP LOCKED",
countQuery="select count(*) from transaction t where t.status = ?1",
nativeQuery = true)
List<Transaction> findByStatusNQ(String status, Integer pageSize);
Same behavior. No locking, both app instances are selecting the same set of data
This is the defined entity:
#Entity
public class Transaction {
#Id
private Long id;
private String description;
private String status;
private String managedBy;
#Temporal(TemporalType.TIMESTAMP)
private Date manageDate;
...
}
The caller service component is annotated with #Transactional to enforce creating new transaction for each execution:
#Transactional(propagation = Propagation.REQUIRES_NEW)
public List<Transaction> updateTrxStatus(String oldStatus,String newStatus){
List<Transaction> trxs = this.executeUsingNQ(oldStatus);
if(trxs.size()>0) {
logger.info( "Start updating Data");
trxs.forEach(transaction -> {
transaction.setStatus(newStatus);
transaction.setManagedBy(instanceName);
transaction.setManageDate(new Date(System.currentTimeMillis()));
});
}else{
logger.info(" Nothing to process");
}
return trxs;
}
#Transactional(propagation = Propagation.REQUIRED)
public List<Transaction> executeUsingNQ(String oldStatus){
List<Transaction> trxs = trxRepo.findByStatusNQ(oldStatus,2);
return trxs;
}
#Transactional(propagation = Propagation.REQUIRED)
public List<Transaction> executeWithPage(String oldStatus){
Pageable firstPageWithTwoElements = PageRequest.of(0, 2);
Page<Transaction> trxs = trxRepo.findByStatus(oldStatus, firstPageWithTwoElements);
return trxs.getContent();
}
Hopefully someone can help identifying whether there is some coding issue or missing coniguration!!!!
It runs that the issue was caused by using an incorrect Dialect with MySql. That version of Dialect "MySQLDialect" assumes "MyISAMStorageEngine" as a default storage engine while creating tables. That engine does not support any type of transactions.
The only storage engine that supports transactions is "InnoDB" which is being selected as the default choice when using other Dialects like "MySQL55Dialect", "MySQL57Dialect" or "MySQL8Dialect"
I am having a weird issue. I have a Domain object:
class MyClass {
String name
Boolean new = true
String number
String type
Byte[] data
Date dateCreated
Date lastUpdated
static belongsTo = [
other: MyOtherClass
]
static mapping = {
table 'my_classes'
data column: "data", sqlType: "MEDIUMBLOB"
}
static constraints = {
data maxSize: 8000 * 66
number nullable: true
}
}
In the Controller I have (edited to show entire method):
def list = {
def myOtherClasses = MyOtherClass.getAll()
if ( !params.max ) params.max = 20
if ( !params.sort && !params.order ) {
params.sort = "new"
params.order= "desc"
}
def myClassCount = MyClass.createCriteria().count() {
'in'( 'other', myOtherClasses )
order( params.sort, params.order )
}
def myClassList = MyClass.createCriteria().list() {
'in'( 'other', myOtherClasses )
order( params.sort, params.order )
}
return [ myClassList: myClassList, myClassCount: myClassCount ]
}
The result if fine and the view is correct. But each time this code runs, the data property isDirty, so version is incremented, and lastUpdated is updated.
The data property is holding audio data, but I don't think that is relevant.
I can't figure out what is going on here. So my question is, how do I make it stop updating?
Using:
Grails 2.4.4
Hibernate 3.6.10.18
MySQL 5.7.9
Thanks in advance :)
After much research and testing, and a few great articles, I have found a solution:
Instead of using type Byte[] in the Domain Object, I use java.sql.Blob, and removed the sqlType in the mapping.
In the controller, I had to make a few changes to access the Byte[] data from the Blob, but that was easy.
I still don't know why this was happening, and I couldn't find any info on it, but it is working as expected now.
I need the MySQL column type for the String field in my Domain class to be TEXT or VARCHAR(3000), but nothing I try seems to work - it remains VARCHAR(255). I've tried
static mapping = {
longString type: 'text'
}
and
static mapping = {
longString sqlType: 'text'
}
and
static constraints = {
longString (blank: true, nullable: true, maxSize: 3000)
}
and
static constraints = {
longString (blank: true, nullable: true, size: 0..65535)
}
MySQL Server version 5.0.95, Grails 2.4.3.
I'm totally mystified and would appreciate any help..
You need to define the type of the column in the mapping block rather than constraints. Assuming the name of the property is longString, add this
static mapping = {
longString type: 'text'
}
This will create a column with a MySQL type of longtext.
To verify that this works, try dropping your database, create a new (empty) database, restart the app and check the type of the column that is created. See this example for reference.
I have an grails application that uses MySQL for authentication purpose and another application that uses MSSQL for database stuff. I need to combine these together as one application. The datasource for MySQL contains the following
dataSource {
pooled = true
driverClassName = "org.h2.Driver"
username = "sa"
password = ""
}
The datasource for application using MSSQL contains the following
dataSource {
pooled = true
driverClassName = "com.microsoft.sqlserver.jdbc.SQLServerDriver" //jdbc driver downloaded from internet: sqljdbc4.jar and sqljdbc_auth.dll (see DisplayHistorical/grails-app/lib)
dialect = "org.hibernate.dialect.SQLServer2008Dialect"
ClassName = "org.hsqldb.jdbcDriver" //Original Code
// enable loggingSql to see sql statements in stdout
loggingSql = true
}
How would I combine these? I looked at the tutorial mentioned on this site (How do you access two databases in Grails) but it doesnt talk about adding the drivers
If you follow the link provided earlier, then you would end up with a datasource configuration like below:
environments {
production {
dataSource_authentication {
pooled = true
url = "jdbc:mysql://yourServer/yourDB"
driverClassName = "com.mysql.jdbc.Driver"
username = "yourUser"
password = "yourPassword"
........
}
dataSource {
pooled = true
driverClassName = "com.microsoft.sqlserver.jdbc.SQLServerDriver"
dialect = "org.hibernate.dialect.SQLServer2008Dialect"
........
}
}
}
Where ever required you can use the authentication datasource explicitly.
How to get it so i return all of the projections from the below
def c = Company.createCriteria()
def a = c.list(params){
projections{
property 'id', property 'name'
}
}
if(a.size() == 0)
render "404"
else {
render (contentType: 'text/json'){
totalCount = a.totalCount
data = a
}
}
The result comes out like this:
{"totalCount":2,"data":["company1","company2"]}
Where i need it to be:
{"totalCount":2,"data":[{"class":"org.example.Company","id":1,"name":"company1"},{"class":"org.example.Company","id":2,"name":"company2"}]}
In the company domain i have lots of relationships (some one to one, one to many etc...)
my domain looks like the following:
package org.example
import java.sql.Timestamp
class Company {
String name
String abn
String cname
String email
String phone
String position
String address
String city
String postcode
int style
int openbookings;
Date date;
int tokenTotal = 0
int totaltokens
int totalboosts
int totalposts
Timestamp tokenstamp
static hasMany = [users: User, broadcast: Broadcast, bookings: Booking, locations: Location,vimsurvey:VimSurvey,rewards: Reward, tokens: CompanyToken]
static constraints = {
abn nullable: true
date nullable: true
style nullable: true
}
}
Any help would be great:)
????
http://grails.org/doc/1.1/ref/Domain%20Classes/createCriteria.html
See the property section under projections: 'property Returns the given property in the returned results'. I don't really get what you are asking for by 'all the projections'.
Are you simply looking to Find all for your domain? Why are you using a projection?
def a = c.list(params){
projections{
property 'id', property 'name'
}
}
should be
def a = c.list(params){
projections{
property 'id'
property 'name'
}
}
In fact, I get a compilation error when I attempt to do it your way. I still feel like it makes more sense to simply get the entire domain itself unless there is a very specific reason not to.