i use grails-1.3.2 and gorm-hbase-0.2.4 plugin.
Sometimes i need to change tables structure(add new tables or columns).
I have created Car table:
class Car{
static belongsTo = [user:User]
String color
String model
//.....
static constraints = {
}
}
but when i want to create car object:
def create = {
Car car = new Car()
car.properties = params
car.save(flush: true)
}
I got the following exception:
ERROR gorm.SavePersistentMethod - APP_CAR
org.apache.hadoop.hbase.TableNotFoundException: APP_CAR
After i run application with create-drop, everithing starts work good..
but i can not after every changes delete all data,
i thought plugin have to do all updates
so, i am looking some way after changind tables structure continue to run application without drop tables..
If anybody know solution please help.
Grails will NOT do automatic updates to your tables, what if it drops a column in production automatically? Maybe that is not what you wanted.
There is a database migration plugin to do this and here is an excellent link that explains it. Note that you need to use grails prod instead of using the ones directly in the link, otherwise it will run in development mode only. The link does not show prod in its commands.
The official links are here and the spring source blog about this is here.
database migration plugin will not be works, because it works only with hibernate.
You need to do some changes in plugin source. HBasePluginSupport.grovy
static doWithApplicationContext = {ApplicationContext applicationContext ->
LOG.debug("Closure HBasePluginSupport.doWithApplicationContext{} invoked with arg $applicationContext")
assert !PluginManagerHolder.getPluginManager().hasGrailsPlugin("hibernate"),"hibernate plug-in conflicts with gorm-hbase plug-in"
// Read data source configuration, setting defaults as required
def dataSource = application.config.dataSource
// TODO write tests for this <--- Even maybe figure out if this is ever invoked
if (!dataSource) dataSource = new HBaseDefaults()
def dbCreate = dataSource?.dbCreate
if (!dbCreate) dbCreate = "create-drop"
LOG.debug("Data Source configured with dbCreate set to $dbCreate")
// TODO Complete dbCreate related processing
if (dbCreate?.toUpperCase()?.equals("CREATE-DROP")) {
def createIndexedTables = dataSource?.indexed
LOG.debug ("Flag createIndexedTables set to $createIndexedTables")
def tableManager = HBaseLookupUtils.getBean("hbase.table.manager")
tableManager.createSequenceTable()
tableManager.createReferenceTable()
application.domainClasses.each {domainClass ->
LOG.debug("Adding table for Domain Class $domainClass")
tableManager.createDomainTable(domainClass, createIndexedTables)
}
LOG.debug("List of all store found :")
tableManager.getTableNames().each {tn ->
LOG.debug("- $tn")
}
} else if (dbCreate?.toUpperCase()?.equals("UPDATE")) {
def createIndexedTables = dataSource?.indexed
def tableManager = HBaseLookupUtils.getBean("hbase.table.manager")
def existingTables = tableManager.getTableNames();
application.domainClasses.each {domainClass ->
LOG.debug("Domain Class $domainClass")
def tableDesc = new HTableDescriptor(HBaseNameUtils.getDomainTableName(domainClass))
if (!existingTables.contains(tableDesc.getNameAsString())) {
tableManager.createDomainTable(domainClass, createIndexedTables)
LOG.debug("Adding table for Domain Class $domainClass")
}
}
}
application.domainClasses.each {domainClass ->
LOG.debug("Adding dbms related methods to Domain Class $domainClass")
def domainClassManager = new HBaseDomainClassManager()
domainClassManager.createQueryMethods(domainClass)
domainClassManager.createPersistenceMethods(domainClass)
domainClassManager.addLazyLoadingSupport(domainClass)
domainClassManager.addDynamicFinders(domainClass)
}
}
Related
I've a Grails 2.5.6 project with a rest controller in which I create n async tasks each of them invoking a method in a service like this:
// MyController
...
def statuses = ['where', 'ownership', 'store']
def tasks = statuses.collect { st ->
task {
return myService.invokeMethod(st, [aDomain, data])
}
}
def props = waitAll(tasks)
...
// MyService
...
store(aDomain, data) {
...
def store = Store.get(data.store)
...
}
If I execute the application, the Store is correctly found in the database.
I've also created an integration test. To avoid conflicts with existing data in the database I create a new Store during the test:
// Integration test (simplified)
....
def store = new Store(....)
store.save(flush: true)
...
def json = [store: store.id] as JSON
...
controller.request.content = params.toString()
controller.request.method = "POST"
controller.update()
...
If I execute the tests, the new store created is not found in the service and the test fails.
I've verified the situation in some points of the application and found that:
- if I search the store in the controller (before or after the tasks are executed), it is found
- if I list all the stores in the service method the new store doesn't exist.
I suppose that this behaviour is due to how hibernate session is handled during tests but I don't know how to solve it.
Any suggestion is welcome.
Thanks
Try using Synchronouse promise factory for tests.
void setup() {
Promises.promiseFactory = new SynchronousPromiseFactory()
}
I have a very simple service set up to create an entry in a Postgres table, and I use it in Bootstrap.groovy for my Grails 3 web-app.
// CompanyService
public Company createCompany(String name) {
Company company = new Company(name: name)
company.save()
return company
}
// BootStrap
def init = {
companyService.createCompany('My Company')
}
Well, at startup I cannot see My Company entry, no matter if the service is transactional or not.
Instead, if using the same line for example in a controller, it works as expected. Am I missing something here?
Have you called your service inside bootstrap?
class BootStrap {
def companyService
def init = { servletContext ->
companyService.createCompany('My Company')
}
}
I have the following domain classes (shortened version)
class TalkingThread {
static hasMany = [comments:Comment]
Set comments = []
Long uniqueHash
}
and
class Comment {
static belongsTo = [talkingThread:TalkingThread]
static hasOne = [author:CommentAuthor]
Long uniqueHash
static constraints = {
uniqueHash(unique:true)
}
}
and
class CommentAuthor {
static hasMany = [comments:Comment]
Long hash
String name
String webpage
}
the following methods
public TalkingThread removeAllComments(TalkingThread thread){
def commentsBuf = []
commentsBuf += thread.comments
commentsBuf.each{
it.author.removeFromComments(it)
thread.removeFromComments(it)
it.delete()
}
if(!thread.save()){
thread.errors.allErrors.each{
println it
}
throw new RuntimeException("removeAllComments")
}
return post
}
public addComments(TalkingThread thread, def commentDetails){
commentDetails.each{
def comment = contructComment(it,thread)
if(!comment.save()){
comment.errors.allErrors.each{ println it}
throw new RuntimeException("addComments")
}
thread.addToComments(comment)
}
return thread
}
Sometimes I need to remove all of the comments from a TalkingThread and add comments that share the same uniqueHashes. So I call the removeAllComments(..) method, and then the addComments(..) method. This causes a
Comment.uniqueHash.unique.error.uniqueHash which caused by a supposedly deleted comment and a 'fresh' comment being added.
Should I be flushing? Maybe there is something wrong with my domain classes?
Edit Expansion of question.
Maybe this is a different question, but I thought that the session has deleted all associations and objects. Therefore the session state is aware that all TalkingThread comments have been deleted. Of course this has not been reflected in the database. I also assumed that the 'saving' of new Comments would be valid given that such 'saving' is consistent with the session state. However such 'saving' would be inconsistent with the database state. Therefore, my understanding of how grails validates objects in relation to session and database state is flawed! Any help in understanding the process of validating saves with respect to session and database states would also be appreciated.
If you want to remove all the Comments from a TalkingThread then you can use Hibernate's cascade behaviour.
Add
static mapping = {
comments cascade: 'all-delete-orphan'
}
to TalkingThread and then you can call comments.clear() followed by thread.save() which will delete the comments that were in the association.
There's a good article on Grails one-to-many-relationships here. The official Grails docs on it are here.
My Integration-Test for my grails application is returning a null object when I try to get a domain object using grails dynamic get method.
This is a simplified example of my problem. Lets say I have a controller TrackerLogController that uses a service TrackerLogService to save an updated Log domain for another Tracker domain.
Domain Tracker:
class Tracker {
int id
String name
static hasMany = [logs: Log]
}
Domain Log:
class Log {
int id
String comment
static belongsTo = [tracker: Tracker]
}
Controller TrackerLogController save:
def TrackerLogService
def saveTrackerLog() {
def trackerId = params.trackerId
def trackerInstance = Tracker.get(trackerId)
Log log = TrackerLogService.saveTrackerLogs(trackerInstance, params.comment)
if( log.hasErrors() ){
//render error page
}
//render good page
}
Service TrackerLogService save:
Log saveTrackerLogs( Tracker tracker, String comment) {
Log log = new Log(tracker: tracker, comment: comment)
log.save()
return log
}
So now I want to write an Integration-Test for this service but I'm not sure if I should be writing one just for the simple logic in the controller (if error, error page else good page) I would think I would write a Unit test for that, and an Integration-Test to check the persistence in the Database.
This is what I have for my Integration-Test:
class TrackerLogServiceTests {
def trackerLogService
#Before
void setUp(){
def tracker = new Tracker(id: 123, name: "First")
tracker.save()
//Now even if I call Tracker.get(123) it will return a null value...
}
#Test
void testTrackerLogService() {
Tacker trackerInstance = Tracker.get(123) //I have tried findById as well
String commit = "This is a commit"
//call the service
Log log = trackerLogService.saveTrackerLogs(trackerInstance , commit)
//want to make sure I added the log to the tracker Instance
assertEquals log , trackerInstance.logs.findByCommit(commit)
}
}
So for this example my trackerInstance would be a null object. I know the Grails magic doesn't seem to work for Unit tests without Mocking, I thought for Intigration-Tests for persistence in the DB you would be able to use that grails magic.
You can't specify the id value unless you declare that it's "assigned". As it is now it's using an auto-increment, so your 123 value isn't used. It's actually ignored by the map constructor for security reasons, so you'd need to do this:
def tracker = new Tracker(name: "First")
tracker.id = 123
but then it would get overwritten by the auto-increment lookup. Use this approach instead:
class TrackerLogServiceTests {
def trackerLogService
private trackerId
#Before
void setUp(){
def tracker = new Tracker(name: "First")
tracker.save()
trackerId = tracker.id
}
#Test
void testTrackerLogService() {
Tacker trackerInstance = Tracker.get(trackerId)
String commit = "This is a commit"
//call the service
Log log = trackerLogService.saveTrackerLogs(trackerInstance , commit)
//want to make sure I added the log to the tracker Instance
assertEquals log , trackerInstance.logs.findByCommit(commit)
}
}
Also, unrelated - don't declare the id field unless it's a nonstandard type, e.g. a String. Grails adds that for you, along with the version field. All you need is
class Tracker {
String name
static hasMany = [logs: Log]
}
and
class Log {
String comment
static belongsTo = [tracker: Tracker]
}
I have a grails application that has a service that creates reports. The report is defined as:
class Report {
Date createDate
String reportType
List contents
static constraints = {
}
}
The service generates a report and populates contents as a list that is returned by createCriteria.
My problem is that my service claims to be saving the Report, no errors turn up, logging says that its all there, but when I go to call show from the controller on that report, it says contents is null.
Another relevant bit, my Service is called by an ActiveMQ message queue. The message originating from my report controller.
Controller:
class ReportController {
def scaffold = Report
def show = {
def rep = Report.get(params.id)
log.info("Report is " + (rep? "not null" : "null")) //says report is not null
log.info("Report content is " + (rep.contents? "not null" : "null")) //always says report.contents is null.
redirect(action: rep.reportType, model: [results: rep.contents, resultsTotal: rep.contents.size()])
}
}
My service that creates the report:
class ReportService {
static transactional = false
static expose = ['jms']
static destination = "Report"
void onMessage(msg)
{
this."$msg.reportType"(msg)
}
void totalQuery(msg)
{
def results = Result.createCriteria().list {
//This returns exactly what i need.
}
Report.withTransaction() {
def rep = new Report(createDate: new Date(), reportType: "totalQuery", contents: results)
log.info("Validation results: ${rep.validate()}")
if( !rep.save(flush: true) ) {
rep.errors.each {
log.error(it)
}
}
}
}
Is there something obvious that I'm missing here? My thought is that since all my unit tests work, that the hibernate context is not being passed through the message queue. But that would generate Exceptions wouldn't it? I've been beating my head on this problem for days, so a point in the right direction would be great.
Thanks,
You can't define an arbitrary List like that, so it's getting ignored and treated as transient. You'd get the same behavior if you had a def name field, since in both cases Hibernate doesn't know the data type, so it has no idea how to map it to the database.
If you want to refer to a collection of Results, then you need a hasMany:
class Report {
Date createDate
String reportType
static hasMany = [contents: Result]
}
If you need the ordered list, then also add in a List field with the same name, and instead of creating a Set (the default), it will be a List:
class Report {
Date createDate
String reportType
List contents
static hasMany = [contents: Result]
}
Your unit tests work because you're not accessing a database or using Hibernate. I think it's best to always integration test domain classes so you at least use the in-memory database, and mock the domain classes when testing controllers, services, etc.