I've got a complex, custom-configured Hibernate setup in Spring (including JPA entities, session factory and data source definitions) that I want to use in Grails 2.1.0. Because of that, I want to give Grails a reference to the sessionFactory and dataSource that I already have. So, i do not want (and in fact, can't) use the hibernate.cfg.xml that's placed in conf/ - nor do I want to use DataSource.groovy, as all the complex configuration is already handled by tested and working code we already have and is all Spring-based.
So, I have managed to get my custom Spring configuration to load on grails run-app (through importBeans() in resources.groovy.) In the logs, I can see the db connection, Spring config and Hibernate starting up just fine, so at runtime the beans to sessionFactory and to the dataSource are created. Now, How do i configure Grails to use those and not try to create its own?
Ideally something like dataSource = ref('myDataSource') somewhere would be great - and the same with sessionFactory = ref('sessionFactory') or similar. I've seen some people putting that in resources.groovy, but it just doesn't work.
I've seen this too:
eventDao(com.JavaClassRequiringDataSource) { dataSource = ref('dataSource') }
but it does not work either (not sure if it ever did.)
Any help would be enormously appreciated … i've spent the last 10 hours trying to get this to work to no avail. I don't mind if I lose some Grails features, as long as it works. The immediate objective is to get GORM to see the (~200) entities we already have and do some scaffolding :)
I also know the entities are not seen by Grails because I've added the following to BootStrap.groovy:
// ...
def grailsApplication
def init = { servletContext ->
println grailsApplication.domainClasses
}
// ...
And it prints [].
If a patch is required, just give me a general idea of where to start and I'll take a look... I just want to get this working.
Thanks!
Update 1:
I've tried several incantations of the resources.groovy file, and currently it looks like this:
beans = {
importBeans('main-spring-file-for-the-rest.xml')
dataSource = ref('dataSource')
}
But when trying to scaffold I still get:
Error 2012-09-06 00:02:00,768 [Thread-9] ERROR plugins.DefaultGrailsPlugin - Cannot generate controller logic for scaffolded class x.y.z.Class. It is not a domain class!
(Log line edited: replaced the actual name of the class with x.y.z.Class.) As I've shown before, the list of entities is empty, and I can see no way of setting up the Hibernate sessionFactory - for example
sessionFactory = ref('sessionFactory')
Doesn't work.
Update 2:
With the beans and entities loading from spring but not being used or seen by GORM, I was able to force the conversion of the entities using a utility built into Grails and a new bean, configured from resources.groovy thusly:
public class TestFix implements ApplicationContextAware {
SessionFactory sessionFactory
ApplicationContext applicationContext
GrailsApplication grailsApplication
def init() {
GrailsHibernateUtil.configureHibernateDomainClasses(sessionFactory, "sessionFactory", grailsApplication)
}
}
beans = {
importBeans('main-spring-file-for-the-rest.xml')
myBean(TestFix) { bean ->
sessionFactory = ref('sessionFactory')
grailsApplication = ref(GrailsApplication.APPLICATION_ID)
bean.initMethod = 'init'
}
}
Now the entities are seen by Grails but scaffolding doesn't work because the augmented domain objects seem to lack the GORM methods (.list() and such.) You would expect GrailsHibernateUtil.configureHibernateDomainClasses() to add those methods in when it creates all the GrailsHibernateDomainClass classes, but either it's failing silently or I'm missing something (perhaps not running early enough? not sure.) Any help very appreciated.
Have you tried the other way around by using the db-reverse-engineer plugin? We had great success on migrating a fairly complex Spring application to grails (approx. 90 Entities).
Related
I'm working on a mutitenant project it maintains different schema for each tenant, followed Project
As we are dynamically switching the tenants so it looks like some configuration is missed which is closing the session or not keeping the session open to fetch the LAZY loaded objects. Which results in "could not initialize proxy - no session" error.
Please check below link to access the complete project and db schema scripts, please follow the steps given in Readme file.
Project
It will be helpful if someone can point out the issue in the code.
i tried to put service methods in #Transactional annotation but that didn't work.
I'm expecting it to make another call to the LAZY loaded object, This project is simplefied verson of the complex project, actually i have lot more lazy loaded objects.
Issue:-
I'm getting no Session error "could not initialize proxy [com.amran.dynamic.multitenant.tenant.entity.Tenant#1] - no Session"
at line 26 (/dynamicmultitenant/src/main/java/com/amran/dynamic/multitenant/tenant/service/ProductServiceImpl.java)
The issue is that your transaction boundaries are not correct. In TenantDatabaseConfig and MasterDatabaseConfig you've correctly added #EnableTransactionManagement, which will setup transactions when requested.
However - the outermost component that has an (implicit) #Transactional annotation is the ProductRepository (by virtue of it being implemented by the SimpleJpaRepository class - which has the annotation applied to it - https://github.com/spring-projects/spring-data-jpa/blob/864c7c454dac61eb602674c4123d84e63f23d766/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java#L95 )
and so your productRepository.findAll(); call will start a transaction, create a JPA session, run the query, close the session, close the transaction, which means that there is no longer any transaction / session open in which to perform the lazy-loading.
Therefore, your original attempt of
i tried to put service methods in #Transactional annotation but that didn't work.
IS the correct thing to do.
You don't say exactly what you tried to do, and where, but there are a few things that could have gone wrong. Firstly, make sure you're adding a org.springframework.transaction.annotation.Transactional and not a javax.transaction.Transactional annotation.
Secondly (and the more likely problem in this scenario), you'll need to configure the annotation with which transaction manager the transaction should be bound to, otherwise it may use an existing / new transaction created against the master DB connection, not the tenant one.
In this case, I think that:
#Service
#Transactional(transactionManager = "tenantTransactionManager")
public class ProductServiceImpl implements ProductService {
should work for you, and make all the methods of the service be bound to a transaction on the tenant DB connection.
EDIT: Answering a follow-up question:
can you please also suggest a better way to inject my tenantTransactionManager in all my service classes, as I don't want to mention tenantTxnManger in all service classes if there is any better way to do it ?
Yes, sure. You can create a meta-annotation that applies multiple other annotations, so you could create:
/**
* Marks class as being a service operating on a single Tenant
*/
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Service
#Transactional("tenantTransactionManager")
public #interface TenantService {
}
and then you can simply annotate your service classes with #TenantService instead of #Service:
#TenantService
public class ProductServiceImpl implements ProductService {
I have a Spring project (not using Spring Boot, if that's relevant) that I'm trying to connect to a local database using the Postgres JDBC driver. (The local database is actually Yugabyte, but that should be fully compatible with the Postgres driver.)
When starting the application, I get this error message:
java.lang.IllegalArgumentException: An AbstractMessageProducingMessageHandler may only be referenced once (org.springframework.integration.config.SplitterFactoryBean#0) - use scope="prototype"
at org.springframework.util.Assert.isTrue(Assert.java:118)
at org.springframework.integration.config.AbstractStandardMessageHandlerFactoryBean.checkReuse(AbstractStandardMessageHandlerFactoryBean.java:168)
at org.springframework.integration.config.AbstractStandardMessageHandlerFactoryBean.createHandler(AbstractStandardMessageHandlerFactoryBean.java:137)
at org.springframework.integration.config.AbstractSimpleMessageHandlerFactoryBean.createHandlerInternal(AbstractSimpleMessageHandlerFactoryBean.java:186)
at org.springframework.integration.config.AbstractSimpleMessageHandlerFactoryBean.getObject(AbstractSimpleMessageHandlerFactoryBean.java:174)
at org.springframework.integration.config.AbstractSimpleMessageHandlerFactoryBean.getObject(AbstractSimpleMessageHandlerFactoryBean.java:59)
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:171)
... 52 more
I can't place this error at all. There is one similar question on Stack Overflow, but there the asker seems to actually know what they're doing and how this is related to spring integration. I, however, am not aware at all that I'm trying to 'reuse' anything. The referenced question also doesn't seem to be related to database configuration.
My setup/configuration is a bit involved, so I'll try to quote the parts that seem relevant.
I have a dao layer project that has the following gradle dependencies (among others):
implementation("org.springframework:spring-context:5.2.2.RELEASE")
implementation("org.springframework:spring-jdbc:5.2.2.RELEASE")
implementation("org.jooq:jooq-kotlin:3.14.11")
runtimeOnly("org.postgresql:postgresql:42.2.19.jre7")
In the same project, I have some configuration (in Kotlin):
#Configuration
open class Config {
#Bean
open fun jdbcTemplate(dataSource: DataSource): JdbcTemplate = JdbcTemplate(dataSource)
#Bean
open fun dslContext(): DSLContext = DefaultDSLContext(SQLDialect.POSTGRES)
#Configuration
#Profile("!unittest")
open inner class NonTestConfig {
#Bean
open fun dataSource(): DataSource {
return DriverManagerDataSource().apply {
// Hardcoded properties to be replaced by values from property file
setDriverClassName("org.postgresql.Driver")
url = "jdbc:postgresql://localhost:5433/demo"
username = "yugabyte"
password = "yugabyte"
}
}
}
}
(Notes: the DSLContext bean is used for JOOQL, included for completeness' sake. The inner class config is there because there is also a separate unit testing config for an embedded database - that one works fine!)
Now, the above project is used in my top-level project that contains the actual application. It's a maven runtime dependency there. I import the config class in this project's XML configuration, using this method:
<context:annotation-config />
<bean class="my.package.Config" />
Then trying to start the application produces the error message.
I figured out what the problem was, but I still don't know how it relates to a <splitter>.
The problem was that the Config class, apart from the database stuff, also included a bean to encrypt data. It turned out that this bean was also defined in another library used by the top-level project. Fixing this duplicate bean problem made the error go away.
I discovered this in a roundabout way: I included the dao project and its configuration in a different top-level project that uses Spring Boot. This led to a clear error message about the encryptor bean having two definitions.
If anyone can explain why the error message is so cryptic in the non-Boot case, that would be a nice complementary answer.
I am creating a new plugin containing CustomService which is intended to replace an existing service from an existing plugin. Following the pattern found in custom security implementations and shown here, I've added the configuration to the resources.groovy, oldService(path.to.new.CustomService). I've also tried adding all injected classes into the closure for this service.
(Actual service names are RegistrationPersonRegistrationCompositeService and NewRegistrationPersonRegistrationCompositeService in code block)
I dont want the original application code to have any reference to the new plugin. However, BuildConfig at the application level will require plugin.location entry. My resource.groovy mods are in the new plugin. I have not had success in this endeavor. Am I modifying the wrong resources.groovy? If this change is required in the original application code, I've lost the ability to leave the original code unaltered. I'm not extending the original Service nor using override annotation. My intent is to replace the service (Spring bean) on start-up. The new plugin has a dependency on the old plugin in an attempt to manage order of operations in loading these classes.
Does it matter that the old service is previously injected in a controller? this would require me to override the controller in the new plugin in the same fashion and inject the correct service for desired behavior?
I've found documentation showing that within a plugin, the resources.groovy will be ignored. Also, building the resources.groovy into a war is problematic. I have not found a solution. I'm getting no error that I can share, just that the desired behavior is missing; the original service is handling the requests.
//was resource.groovy - now renamed to serviceOverRide.groovy - still located in \grails-app\conf\spring of plugin
//tried this with and without the BeanBuilder. Theory: I'm missing the autowire somehow
import org.springframework.context.ApplicationContext
import grails.spring.BeanBuilder
def bb = new BeanBuilder()
bb.beans {
registrationPersonRegistrationCompositeService(path.to.services.registration.NewRegistrationPersonRegistrationCompositeService) { bean ->
bean.autowire = true
registrationRestrictionCompositeService = ref("registrationRestrictionCompositeService")
registrationPersonTermVerificationService = ref("registrationPersonTermVerificationService")
}
classRegistrationController(path.to.services.registration.ClassRegistrationController) { bean ->
bean.autowire = true
selfServiceLookupService = ref("selfServiceLookupService")
registrationPersonRegistrationCompositeService = ref("registrationPersonRegistrationCompositeService")
}
}
ApplicationContext appContext = bb.createApplicationContext()
Additional information: Added the following lines to the PluginGrailsPlugin.groovy. The original service is still handling these requests
def dependsOn = ['appPersonRegistration': '1.0.20 > *']
List loadAfter = ['appPersonRegistration']
def doWithSpring = {
registrationPersonCourseRegistrationCompositeService(path.to.new.registration.TccRegistrationPersonCourseRegistrationCompositeService)
}
def doWithApplicationContext = { applicationContext ->
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL)
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext.getBeanFactory()
beanFactory.registerBeanDefinition("registrationPersonCourseRegistrationCompositeService", BeanDefinitionBuilder.rootBeanDefinition(TccRegistrationPersonCourseRegistrationCompositeService.class.getName()).getBeanDefinition())
}
I highly recommend you read the section of the documentation on Plugins. The reason why I recommend this is because plugins:
Do not include, or make use of resources.groovy
Provide a means through doWithSpring to effect the spring application
Following the information in the documentation you should have no issue overriding the service in the application context.
You must implement your changes to the application context using doWithSpring this is the key to solving your issues.
In this implementation, I had a utility method in a service for which I was attempting to provide an override. Problem is, the Aspect works as a proxy and must override a method that is called directly from another class. In my classRegistrationController, I was calling service processRegistration() which in turn called applyRules(). Example-only method names used. Since the service was calling its own utility, there was no opportunity for the proxy/wrapper to circumvent the call to applyRules(). Once this was discovered, I refactored the code in this fashion: Controller calls processRegistration as it always had. After returning, another call is made to the service, processLocalRules(). The new method is an empty placeholder intended to be overridden by the client's custom logic. The plugin with Aspect works now using resources.groovy. I prefer the doWithSpring as Joshua explained for this reason: my intent to get the plugin to work without modification to the original app-config; otherwise resource.groovy is a valid approach. Upvoting Joshua's answer as it does satisfy the requirement and is cleaner. Thanks!
From our Grails/Groovy app we need to use a service from a legacy Java service class, the constructor of which has a parameter of type org.springframework.core.io.Resource, e.g.
public ServiceClass(Resource someResource)
We need to inject an instance of the service class into a Groovy class of our app using Spring DSL, with the Resource referring to an XML file within our /src/main/resources. I tried to create the Spring config for this purpose, but so far I couldn't find a working solution. The relevant part of the config file looks like this
beans = {
xmlns aop:"http://www.springframework.org/schema/aop",
sec:"http://www.springframework.org/schema/security",
context:"http://www.springframework.org/schema/context"
serviceClass(com.somepackage.ServiceClass) {
//here we need to refer to the constructor arg XML file some way
}
}
I have tried multiple syntaxes found in various tutorials, e.g. closure for beanDefinition.constructorArgs, but unfortunately without success so far. Although neither the app compilation (grails:war) nor the startup (grails:run-app) indicates any problems with the bean wiring, when the app is actually loaded into the browser, we receive a NPE stating that our Groovy class into which the service class is injected, is a null object. So it seems that the bean wiring was not successful after all. Any help is appreciated
After fixing various issues with the project setup itself and multiple cleanups/recompiles, it seems that the following two approaches are both OK
serviceClass(com.somepackage.ServiceClass, '/WEB-INF/constructor-arg-xml-file.xml') {}
and
serviceClass(com.somepackage.ServiceClass) { bean ->
bean.constructorArgs = [
'/WEB-INF/constructor-arg-xml-file.xml'
]
}
I'm using Hibernate to map objects to a legacy schema which contains some ginormous tables via annotations (as XML files are so 2003). Since these classes are so large, yes I occasionally make an occasional typo, which Hibernate doesn't bother to tell me about until I try to run it.
Here's what I've tried:
One: Setting hbm2ddl.auto to "validate":
This causes the String values of the class to validate against varchar(255). Since many of the column types in the database are CHAR(n), this blows up. I would have to add the columnDefinition="CHAR(n)" to several hundred mappings.
Two: Using Unitils.
Importing these via Maven causes imports of dependency libraries which blow up other sections of code. Example: I'm using Hibernate 4.1, but Unitils imported Hibernate 3.2.5 and blew up a UserType.
So, is there another way to do this? I looked at the Unitils code to see if I could simply yank the sections I needed (I do that with apache-commons fairly often when I just need a single method), but that's not a simple task.
Hibernate is configured via a Spring application context.
Any ideas out there?
I would write tests against an in-memory database (HSQLDB, H2) using the Spring testing framework. You'll quickly see any mapping errors when you attempt to run queries against the tables.
The test class would look something like this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes=MyTestConfig.class)
#TransactionConfiguration(transactionManager="txMgr", defaultRollback=true)
public class MyTest {
#Autowired
private SessionFactory sessionFactory;
// class body...
}
I would configure Hibernate to auto-deploy the tables as part of the tests.