I'm running a small, trivial Grails 3.3.0 application using a H2 file based database. For simple backup reasons I would like to dump the current database state to a file using the H2 specific SCRIPT command:
SCRIPT TO /path/to/backup/dir/tempoDb.sql;
Currently I am trying to execute the native SQL command like this.
User.withSession { session ->
NativeSQLQuerySpecification nativeSQLQuerySpecification = new NativeSQLQuerySpecification("SCRIPT TO /path/to/backup/dir/tempoDb.sql;", null, null)
session.executeNativeUpdate(nativeSQLQuerySpecification, new QueryParameters())
}
but this does not work.
You can autowire the dataSource and try to run your sql query using the connection obtained from datasource Without going through Hibernate. The dataSource bean is registered in the Grails Spring context and it is an instance of javax.sql.DataSource.
Here is an example of a Grails service that backup the current H2 database to the file system.
#ReadOnly
class BackupService {
DataSource dataSource
def backup() {
def sql = "SCRIPT DROP TO '${System.properties['java.io.tmpdir']}/backup.sql'"
Statement statement = dataSource.connection.createStatement()
boolean result = statement.execute(sql)
}
}
Related
I am trying to build a small POC with JetpackCompose Desktop and SQLDelight. I want that the data is persisted even after the application is restarted (not only in memory as all the tutorial examples I encountered show), so I tried this:
// ArticlesLocalDataSource.kt
class ArticlesLocalDataSource {
private val database: TestDb
init {
val driver: SqlDriver = JdbcSqliteDriver(url = "jdbc:sqlite:database.db")
TestDb.Schema.create(driver)
database = TestDb(driver)
}
// ...
}
When I run the application for the first time this works, i.e: a database.db file in the project root is created and the data is stored successfully.
However, when I try to run the application a second time, then it crashes immediately with:
Exception in thread "main" org.sqlite.SQLiteException: [SQLITE_ERROR] SQL error or missing database (table ArticleEntity already exists)
at org.sqlite.core.DB.newSQLException(DB.java:1012)
at org.sqlite.core.DB.newSQLException(DB.java:1024)
at org.sqlite.core.DB.throwex(DB.java:989)
at org.sqlite.core.NativeDB.prepare_utf8(Native Method)
at org.sqlite.core.NativeDB.prepare(NativeDB.java:134)
at org.sqlite.core.DB.prepare(DB.java:257)
at org.sqlite.core.CorePreparedStatement.<init>(CorePreparedStatement.java:45)
at org.sqlite.jdbc3.JDBC3PreparedStatement.<init>(JDBC3PreparedStatement.java:30)
at org.sqlite.jdbc4.JDBC4PreparedStatement.<init>(JDBC4PreparedStatement.java:25)
at org.sqlite.jdbc4.JDBC4Connection.prepareStatement(JDBC4Connection.java:35)
at org.sqlite.jdbc3.JDBC3Connection.prepareStatement(JDBC3Connection.java:241)
at org.sqlite.jdbc3.JDBC3Connection.prepareStatement(JDBC3Connection.java:205)
at com.squareup.sqldelight.sqlite.driver.JdbcDriver.execute(JdbcDriver.kt:109)
at com.squareup.sqldelight.db.SqlDriver$DefaultImpls.execute$default(SqlDriver.kt:52)
at com.vgrec.TestPlus.TestDbImpl$Schema.create(TestDbImpl.kt:33)
at com.vgrec.data.local.ArticlesLocalDataSource.<init>(ArticlesLocalDataSource.kt:20)
I understand that it's crashing because there's an attempt to create the database again, but the database already exists. What is not clear to me, is how do I connect to the DB if a DB already exists?
For completeness, here's the build file:
// build.gradle
plugins {
kotlin("jvm") version "1.6.10"
id("org.jetbrains.compose") version "1.1.0"
// ...
id("com.squareup.sqldelight") version "1.5.3"
}
sqldelight {
database("TestDb") {
packageName = "com.test"
}
}
dependencies {
implementation(compose.desktop.currentOs)
// ..
implementation("com.squareup.sqldelight:sqlite-driver:1.5.4")
implementation("com.squareup.sqldelight:coroutines-extensions-jvm:1.5.4")
}
OK, so in the end I decided to just check if the database file exists and invoke the creation only if it does not exist.
Something like this:
init {
val driver: SqlDriver = JdbcSqliteDriver("jdbc:sqlite:database.db")
if (!File("database.db").exists()) {
TestDb.Schema.create(driver)
}
// ...
}
From first glance this seems to work as expected, but I am not sure this is the recommended approach as I am very new to SQLDelight, so other suggestions are welcome.
Help!
Our plugin project integration tests should hit the database specified in the datasource.groovy, but for some reason they ignore it, and do it in memory.
Its a plugin which provides the core services (i.e. DB access) to several grails apps which are each a grails application.
Datasource.groovy looks like this:
dataSource {
pooled = true
driverClassName = "com.mysql.jdbc.Driver"
dialect = "org.hibernate.dialect.MySQL5InnoDBDialect"
}
environments {
development {
dataSource {
dbCreate = "create-drop"
url = "jdbc:mysql://127.0.0.1:3306/db"
username = "someuser"
password = "somepass"
}
}
test {
dataSource {
dbCreate = "update"
url = "jdbc:mysql://127.0.0.1:3306/db"
username = "someuser"
password = "somepass"
}
}
production {
dataSource {
}
}
}
The test (SiteIntegrationSpec.goovy)
import grails.test.mixin.TestFor
import grails.test.spock.IntegrationSpec
#TestFor(Site)
class SiteIntegrationSpec extends IntegrationSpec {
static transactional = false
def setup() {
}
def cleanup() {
}
void "test something"() {
when:
Site site
site = new Site(name: "asdf", description: "asdfsd").save(failOnError: true)
then:
site.id == 3
when:
Site site2 = Site.get(1L)
then:
site2.name == "root"
}
}
Data already existing in the site table:
ID name description
1 root root
2 test test
The first test should insert a record which will happen to have an ID of 3. It actually inserts with an ID of 1, i.e. its not seeing or hitting the test database, its using some mock or internal db which is not defined anywhere.
The second test fails as instead of retrieving "root" it retrieves "asdf"
What I have tried:
creating a separate DB for test. Didn't help.
specifying -Dgrails.env=test when running tests. Didn't help
running the tests with the DB down. This correctly fails with cant create pool type exception.
changing the test datasource password to an incorrect one - this correctly throws an exception.
grails -Dgrails.env=test test-app com.me.myproject.SiteIntegrationSpec --stacktrace --verbose
So grails is connecting to the test datasource, but then the integration tests are not using it!
Any ideas?
Edit: Site is a domain object:
class Site {
String name
String description
}
Plugin DataSource.groovy files aren't included in the plugin zip, and if you somehow manually or programmatically include them, they'll be ignored. The same goes for Config.groovy, UrlMappings.groovy, and BootStrap.groovy. In general when something is usable from a plugin, if the application has a file with the same name and location, it overrides the plugin's file, so that would keep this from working also.
You could define a dataSource bean in your plugin's doWithSpring that replaces the one Grails creates based on DataSource.groovy that uses values from a file that exists in the plugin zip, or that is located in the application if that makes sense. Note that there are really 3 DataSource beans and two of them are proxies of the "real" one, so you need to define yours as dataSourceUnproxied so the other two proxy yours and retain the behavior that they add.
Another thing that you will need to fix once you resolve this is your use of unit test annotations in an integration test. Never use Mock, TestFor, or any unit test mixin annotation or base class, since their purpose is to establish a fairly realistic environment that makes up for Spring, Hibernate, installed plugins, and lots of Grails functionality not being available, but in an integration test they are available, and the unit test stuff will stomp on the real instances.
Also - why are you using static transactional = false? This disables an important integration test feature where all of your test methods run in a transaction that is rolled back at the end of the tests pass or fail. This ensures that nothing you do in a test influences other tests - everything is independent. If you disable this, you need to undo all of the changes, and it's easy to miss some and introduce false negatives or worse - false positives - into your tests.
I am having a problem getting Liquibase changeset contexts to play nicely within my Grails application. I have a set of changesets that I would like to only run within a "test" context. However, they are executing every time. I think I have a configuration problem.
The Liquibase documentation states that you just have to add the context="test" attribute to your changeSet. For my proof of concept test I am going to create an insert of a Patient record that I want to insert on Test, but not in my local Development environment. My changeset has the context added:
<changeSet id="v1.1-garbage-1" author="Eric" context="test">
<insert tableName="patient">
[...]
</insert>
</changeSet>
And then within my DataSource.groovy file I define my environments:
environments {
development {
dataSource {
dbCreate = "create"
jndiName = "java:comp/env/jdbc/mydatabasename"
}
}
test {
dataSource {
dbCreate = "create"
url = "jdbc:h2:mem:testDb;MVCC=TRUE;LOCK_TIMEOUT=10000"
}
}
[...]
So I have two environments, development and test. Then in my Config.groovy, I set up the Grails databasemigration plugin to only have the context "development" (for this proof of concept):
// Database Migration plugin
grails.plugin.databasemigration.updateOnStart = true
grails.plugin.databasemigration.updateOnStartFileNames = ['changelog.xml']
grails.plugin.databasemigration.autoMigrateScripts = ['RunApp', 'TestApp']
grails.plugin.databasemigration.changelogFileName = "changelog.xml"
grails.plugin.databasemigration.development.updateOnStartContexts = ['development']
In that final line, as I understand it, I am telling the databasemigration plugin to set the "development" contexts to 'development', thus when Liquibase executes, it should not run my changeset above, because it is defined within the 'test' context.
Yet when I run the application, my changeset is executed. What have I bungled or missed in the setup?
My bet is that the last config line is not doing what you expect.
According to the "Multiple DataSource Example" section at http://grails-plugins.github.io/grails-database-migration/docs/manual/guide/3%20Configuration.html this syntax is used for multiple data sources. So, in your case the updateOnStartContexts parameter is going to be applied to a datasource named dataSource_development which you obviously don't have...
Can you try instead the following:
environments {
development{
grails.plugin.databasemigration.updateOnStartContexts = ['development']
}
}
When I try run this script to secure my web services on Grails / CXF client I get
"Cannot invoke method getInInterceptors() on null object" on secureServiceFactory
Does secureServiceFactory need to be set somewhere else?
Any ideas:
Code :
class BootStrap {
def secureServiceFactory
def init = { servletContext ->
Map<String, Object> inProps = [:]
inProps.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
inProps.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
Map<QName, Validator> validatorMap = new HashMap<QName, Validator>();
validatorMap.put(WSSecurityEngine.USERNAME_TOKEN, new UsernameTokenValidator() {
#Override
protected void verifyPlaintextPassword(org.apache.ws.security.message.token.UsernameToken usernameToken, org.apache.ws.security.handler.RequestData data)
throws org.apache.ws.security.WSSecurityException {
if(data.username == "wsuser" && usernameToken.password == "secret") {
println "username and password are correct!"
} else {
println "username and password are NOT correct..."
throw new WSSecurityException("user and/or password mismatch")
}
}
});
inProps.put(WSS4JInInterceptor.VALIDATOR_MAP, validatorMap);
secureServiceFactory.getInInterceptors().add(new WSS4JInInterceptor(inProps))
}
Not sure this is a total answer, but, I receive the same errors and I understand that the cxf plugin is meant to wire up service factories that will match the name of your exposed service. I have verified that out of the box, running the grails-cxf plugin using grails run-app the application works. however, by executing grails war on the project creates a war that when deployed to tc server [vfabric-tc-server-developer-2.9.4.RELEASE] tomcat 7 [tomcat-7.0.47.A.RELEASE], this error occurs.
It is also useful to note that out of the box, as the plugin author has noted in other references [http://www.christianoestreich.com/2012/04/grails-cxf-interceptor-injection/] the generated war won't work unless you change test('org.apache.ws.security:wss4j:1.6.7') to compile('org.apache.ws.security:wss4j:1.6.7') and I note that I was unable to make that work, I had to use compile('org.apache.ws.security:wss4j:1.6.9')
Unfortunately, after surpassing this, I run into a third error when deploying the war that doesn't occur in grails run-app:
22-Aug-2014 11:46:05.062 SEVERE [tomcat-http--1] org.apache.catalina.core.StandardWrapperValve.invoke Allocate exception for servlet CxfServlet
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'cxf' is defined
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:641)
at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1159)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:282)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:273)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:979)
at org.apache.cxf.transport.servlet.CXFServlet.loadBus(CXFServlet.java:75)
I'll continue looking at it, but perhaps this war isn't meant to really deploy, but is more meant just for development of the plugin itself. however, if that is the case, it would still be better to work in TC because then we can leverage the code in our own projects with confidence.
I created the application using the instructions here:
http://blog.armbruster-it.de/2009/10/example-neo4j-with-grails/
I then added to DataSource.groovy this:
grails {
neo4j {
type = "embedded"
location = "/usr/local/Cellar/neo4j/"
params = []
}
}
Where my graph.db is located at /usr/local/Cellar/neo4j/community-1.8.1-unix/libexec/data/graph.db
What should be going into this location. I am adding new ndoes but when I run
start n=node(*) return n;
in the shell there is no new data. Thanks!
I believe your location should point right up to
/usr/local/Cellar/neo4j/community-1.8.1-unix/libexec/data/graph.db
since that is where your neo4j database is located