I'm writing a simple bean that I want to configure with a table name, an XML file with some data, so that if at application startup the table is empty it get initialized with that data. I decided to use simple SQL queries, but I can't get a session from the sessionfactory, because it says:
Error creating bean with name 'vecchiOrdiniFiller' defined in ServletContext resource [/WEB-INF/spring/servlet-context.xml]: Invocation of init method failed; nested exception is org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
But this is the configuration (very similar to a service):
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="mySessionFactory" />
</bean>
<tx:annotation-driven />
<bean id="ordiniVecchioSistemaLoader" class="it.jsoftware.jacciseweb.assistenza.common.ExcelXmlDataLoader">
<property name="xmlFileName" value="WEB-INF/data/daticlientijaccisemarco.xml"></property>
</bean>
<bean id="vecchiOrdiniFiller" class="it.jsoftware.jacciseweb.assistenza.common.BaseTableFiller" init-method="init">
<property name = "sessionFactory" ref = "mySessionFactory"></property>
<property name="loader" ref="ordiniVecchioSistemaLoader"></property>
<property name="tableCreationString" value="CREATE TABLE `vecchiordini` ( `ID` INT(11) NOT NULL AUTO_INCREMENT, `codicejazz` VARCHAR(255) DEFAULT NULL, `progressivolicenza` INT(11), `codicearticolo` VARCHAR(255) DEFAULT NULL, `rivenditore` VARCHAR(255) DEFAULT NULL, `cliente` VARCHAR(255) DEFAULT NULL, PRIMARY KEY (`ID`)) ENGINE=INNODB DEFAULT CHARSET=utf8"></property>
<property name="table" value="vecchiordini"></property>
<property name="tableColumns">
<list>
<value>codicejazz</value>
<value>progressivolicenza</value>
<value>codicearticolo</value>
<value>rivenditore</value>
<value>nomecliente</value>
</list>
</property>
<property name="loaderColumns">
<list>
<value>clicod</value>
<value>licsmatricola</value>
<value>artcod</value>
<value>rivenditore</value>
<value>cliente</value>
</list>
</property>
</bean>
and I annotated the init() method with #Transactional. But it doesn't start a transaction and I get that error:
#Transactional
public void init() throws Exception {
logger.info("BaseTableFilter per tabella: " + table + ", usando: "
+ loader.getSourceName());
Session session = dao.getSession();
Transaction tx = session.beginTransaction();
...
why doesn't that work?
The init-method or #PostConstruct annotated methods are not proxied, so you will not be able to use #Transactional on that. You should use #Transactional on your service, why is that not suitable for you?
Quoted from the SpringSource Jira:
This is as defined, actually: init methods (such as #PostConstruct methods) are always called on the target instance itself. The proxy will only be generated once the target instance has been fully initialized... In other words, the #Transactional proxy isn't even created at the point of the #PostConstruct call yet.
Switching to mode="aspectj" would help since it weaves the target class directly, in which case the init method will have been modified for transactional awareness at the time of the container init call already.
I guess at the very minimum, we should document the limitations of #Transactional on proxies more clearly - and point out where mode="aspectj" might be a solution.
As stated, you can try the mode="aspectj" but rather you should review your design in my opinion.
I don't think you can make the init method transactional. But you can invoke another transactional method of another service from it.
Related
I'm using last version of Jasig CAS server (4.0.0) with an LDAP server.
Users are stored under this LDAP structure : ou=Users,ou=SSOTEST,dc=mycompany,dc=com
What I want is to search an user from a top level (example : ou=SSOTEST,dc=mycompany,dc=com).
CAS server has an LdapPersonAttributeDao bean which is looking for an object matching a search filter. Here is the code for this bean :
<bean id="ldapPersonAttributeDao"
class="org.jasig.cas.persondir.LdapPersonAttributeDao"
p:connectionFactory-ref="searchPooledLdapConnectionFactory"
p:baseDN="ou=SSOTEST,dc=company,dc=com"
p:searchControls-ref="searchControls"
p:searchFilter="uid={0}">
<property name="resultAttributeMapping">
<map>
<!--
| Key is LDAP attribute name, value is principal attribute name.
-->
<entry key="memberOf" value="userMemberOf" />
<entry key="cn" value="userCn" />
</map>
</property>
</bean>
And now the searchControls bean which do a lookup at SUBTREE_SCOPE (2) level (according toSearchControls scope level values).
<bean id="searchControls"
class="javax.naming.directory.SearchControls"
p:searchScope="2"
p:countLimit="10" />
When I run my CAS server and I try to authenticate, everything works but there are no extra attributes returned.
I think the problem comes from searchScope, which don't seems to be set to wanted value.
Here is output log from the server :
<execute request=[org.ldaptive.SearchRequest#-1312441815::baseDn=ou=SSOTEST,dc=mycompany,dc=com, searchFilter=[org.ldaptive.SearchFilter#-3391
91059::filter=uid={0}, parameters={0=myuser}], returnAttributes=[], searchScope=null, timeLimit=0, sizeLimit=10 [...]
I know its been some time since this question was asked. But I managed to fix this problem by adding:
<bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor" />
to deployerConfigContext.xml.
The cause of this issue was that the initalize method in LdapPersonAttributeDao was not being invoked because the #PostConstruct annotation wasn't being executed. For this reason the searchScope variable was never set.
I've been having trouble with Camel transactions and after some great help from the camel list I eventually tracked it down to using org.apache.activemq.camel.component.ActiveMQComponent in a bean with id 'activemq'. If I use an id of 'activemqTx' it works. But I can't seem to find any documentation on the significance of 'activemqTx' as a bean id.
only one message gets through the route with this:
<bean id="activemq" class="org.apache.activemq.camel.component.ActiveMQComponent">
everything works with this:
<bean id="activemqTx" class="org.apache.activemq.camel.component.ActiveMQComponent">
Must have been a bean name clash
Hi I have a issue respect a hbm2ddl.import_files, it seems that don't work and not seems to appear in the log.
this is my configuration:
<property name="hibernateProperties">
<value>
hibernate.dialect=${hibernate.dialect}
hibernate.default_schema=${hibernate.default_schema}
hibernate.jdbc.batch_size=${hibernate.jdbc.batch_size}
hibernate.show_sql=${hibernate.show_sql}
hibernate.hbm2ddl.auto=${hibernate.hbm2ddl.auto}
hibernate.id.new_generator_mappings=${hibernate.id.new_generator_mappings}
hibernate.hbm2ddl.import_files=${hibernate.hbm2ddl.import_files}
<!-- Auto Generated Schemas and tables not good for production
hibernate.hbm2ddl.auto=update-->
</value>
</property>
the hibernate.hbm2ddl.import_files=/import.sql, and the file is:
insert into DEPARTAMENTO (NOMBRE_DEPART,REFERENCIA_DEPART) values ('AMAZONAS')
The jdbc.properties:
#org.hibernate.dialect.PostgreSQLDialect
hibernate.default_schema = "DBMERCANCIAS"
hibernate.show_sql = true
hibernate.id.new_generator_mappings = true
hibernate.hbm2ddl.auto = create
hibernate.jdbc.batch_size = 5
#Default the factory to use to instantiate transactions org.transaction.JDBCTransactionFactory
hibernate.transaction.factory_class=org.transaction.JDBCTransactionFactory
#Initialize values statements only on create-drop or create
hibernate.hbm2ddl.import_files = /import.sql
The database is postgresql 9.1.1, spring 3.1.0.RELEASE and hibernate 4.1.2.Final, the hibernate.hbm2ddl.auto is set to "create", the tables and the schema create but not run sql command insert why?, I can see in the log where this command run.
My error was the location in hibernate properties.
hibernate.hbm2ddl.import_files = /META-INF/spring/import.sql
is the correct location.
You could put import.sql in classpath(/classes/import.sql) and remove property hibernate.hbm2ddl.import_files from hibernate configuration/property.
NOTE: hibernate.hbm2ddl.auto must to create
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
<prop key="hibernate.hbm2ddl.auto">create</prop>
</property>
</bean>
(Context: I'm an experienced programmer, but new to LDAP, AD and Spring.)
We are a Windows shop, so all of our authentication is done with Active Directory. We are attempting to integrate a third-party product that is written in Java, so it does all of its authentication using Spring Security. So far, so good -- they've done that integration before, and there's a good deal online about how to set things up.
The problem is, our AD setup is a bit complex: in particular, our user accounts exist in various nodes in the AD/LDAP tree. To give a simplified example, say the LDAP tree looks like this:
DC=my-domain,DC=com
+ CN=Users
++ CN=user1,CN=Users,DC=my-domain,DC=com
+ CN=Staff
++ CN=user2,CN=Staff,DC=my-domain,DC=com
The thing is, all of the examples I have found let me authenticate either user1 or user2, but not both. That is, the following XML snippet will work to authenticate user1 against roles defined under "Groups":
<security:ldap-server url="ldap://my-domain.com:389" manager-dn="CN=manager_svc,OU=System Users,DC=my-domain,DC=com" manager-password="MyPa55w0rd"/>
<security:ldap-authentication-provider
user-dn-pattern=""
user-search-base="CN=Users,DC=my-domain,DC=com"
user-search-filter="(&(sAMAccountName={0})(objectclass=user))"
group-search-base="OU=Groups,DC=mydomain,DC=com"
group-search-filter="member={0}"
/>
but that won't authenticate user2, since he doesn't match the user-search-base. Contrariwise, I can change user-search-base to CN=Staff,DC=my-domain,DC=com, which will work for user2, but then it won't work for user1.
So the question is, how do I make this search work for user accounts that are scattered across the AD/LDAP tree? I can imagine two possibilities, but I haven't figured out how to do either yet:
On the one hand, if I can make user-search-base multi-valued, that solves my problem easily and correctly: I just put in all of the locations where user accounts might be found. So far, all of my attempts to do this have met with one error or another, but I'm still experimenting.
OTOH, there is Subtree scoping of the search. I can see in the interactive LDAP tools that search can be either single-level or subtree. Far as I can tell, Spring out of the box is doing single-level. I can see that the underlying FilterBasedLdapUserSearch class has a setSearchSubtree() method, which looks like what I want, but I can't find a way to set that to true from the XML. (For now, let's assume that it isn't feasible to change the underlying Java program.)
The first option would be ideal, since it is probably much more efficient, but if that isn't possible and the second is, I suspect we can make it work.
I have a suspicion that the second approach is possible using thorny bean hackery, but I know next to nothing about beans, so I'd rather not wade into those thickets by myself. Does anybody have a good recipe to recommend?
Thanks much for any guidance you can provide...
I solved this by using a searchBase with the empty string value (this uses the root as the searchbase, just like prule's answer), but I also had to set the property "referral" to "follow", otherwise I got a PartialResultException!
DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(...);
contextSource.setReferral("follow");
You could try searching from the domain root, if that is feasible, though that can cause problems with AD.
Alternatively, use of explicit bean configuration is probably your best option. You can inject a custom LdapUserSearch implementation into the BindAuthenticator bean, which searches under all the necessary locations. If you look at the example in the docs, it shows a FilterBasedLdapUserSearch configuration. You could either use a couple of these, or implement the interface yourself from scratch. Here's a quick hack as an example:
public class CustomLdapSearch implements LdapUserSearch {
public static final String SAM_FILTER="(&(sAMAccountName={0})(objectclass=user))"
final LdapUserSearch users;
final LdapUserSearch staff;
public CustomLdapSearch(BaseLdapPathContextSource contextSource) {
users = new FilterBasedLdapUserSearch("CN=Users,DC=my-domain,DC=com", SAM_FILTER, contextSource);
staff = new FilterBasedLdapUserSearch("CN=Staff,DC=my-domain,DC=com", SAM_FILTER, contextSource);
}
public DirContextOperations searchForUser(String username) {
try {
return users.searchForUser(username);
} catch(UsernameNotFoundException e) {
return staff.searchForUser(username);
}
}
}
Then change the BindAuthenticator configuration to:
<bean class="org.springframework.security.ldap.authentication.BindAuthenticator">
<constructor-arg ref="contextSource"/>
<property name="userSearch" ref="customSearch"/>
</bean>
<bean id="customSearch" class="CustomLdapSearch">
<constructor-arg ref="contextSource"/>
</bean>
I've done something similar with spring-security-2.0.x using FilterBasedLdapUserSearch - where users were spread across multiple nodes:
<bean id="ldapUserSearch" class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
<constructor-arg value=""/> <!-- optional sub-tree here -->
<constructor-arg value="(&(sAMAccountName={0})(objectclass=user))"/>
<constructor-arg ref="contextSource"/>
</bean>
<bean id="ldapAuthProvider"
class="org.springframework.security.providers.ldap.LdapAuthenticationProvider">
<constructor-arg>
<bean class="org.springframework.security.providers.ldap.authenticator.BindAuthenticator">
<constructor-arg ref="contextSource"/>
<property name="userSearch" ref="ldapUserSearch"/>
</bean>
</constructor-arg>
<property name="userDetailsContextMapper" ref="userDetailsContextMapper"/>
</bean>
<bean id="contextSource"
class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
<constructor-arg value="ldap://localhost:10389/CN=Users,DC=my-domain,DC=com"/>
<!-- you may or may not need to connect with an account that can search -->
<!--<property name="userDn" value="uid=admin,ou=system"/>-->
<!--<property name="password" value="secret"/>-->
</bean>
I've tried adding bulk inserting to my application, but the Batcher is still NonBatchingBatcher with a BatchSize of 1.
This is using C#3, NH3RC1 and MySql 5.1
I've added this to my SessionFactory
<property name="adonet.batch_size">100</property>
And my code goes pretty much like this
var session = SessionManager.GetStatelessSession(type);
var tx = session.BeginTransaction();
session.Insert(instance);
I'm using HILO identity generation for the instances in question, but not for all instances on the database. The SessionFactory.OpenStatelessSession doesn't take a type, so it can't really know it can do batching on this type, or...?
After some digging into NHibernate, I found something in SettingsFactory.CreateBatcherFactory that might give some additional info
// It defaults to the NonBatchingBatcher
System.Type tBatcher = typeof (NonBatchingBatcherFactory);
// Environment.BatchStrategy == "adonet.factory_class", but I haven't
// defined this in my config file
string batcherClass = PropertiesHelper.GetString(Environment.BatchStrategy, properties, null);
if (string.IsNullOrEmpty(batcherClass))
{
if (batchSize > 0)
{
// MySqlDriver doesn't implement IEmbeddedBatcherFactoryProvider,
// so it still uses NonBatchingFactory
IEmbeddedBatcherFactoryProvider ebfp = connectionProvider.Driver as IEmbeddedBatcherFactoryProvider;
Could my configuration be wrong?
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2" >
<session-factory name="my application name">
<property name="adonet.batch_size">100</property>
<property name="connection.driver_class">NHibernate.Driver.MySqlDataDriver</property>
<property name="connection.connection_string">my connection string
</property>
<property name="dialect">NHibernate.Dialect.MySQL5Dialect</property>
<property name="proxyfactory.factory_class">NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle</property>
<!-- To avoid "The column 'Reserved Word' does not belong to the table : ReservedWords" -->
<property name="hbm2ddl.keywords">none</property>
</session-factory>
</hibernate-configuration>
I know this question is a year old, but there is a NuGet package that adds MySQL batching functionality to NHibernate. The reason that it's not baked directly into NHibernate is that the functionality required a reference to the MySQL.Data assembly, and the dev team didn't want the dependency.
IIRC, batching is currently supported for Oracle and SqlServer only.
As almost any other aspect of NH, this is extensible, so you can write your own IBatcher/IBatcherFactory and inject them via configuration.
Sidenote: current version of NH is 3.0 GA.
Really old question but...let's be completely correct
Another reason for batching not working can be use of stateless session (as in your case). Stateless session does not support batching. From documentation:
The insert(), update() and delete() operations defined by the
StatelessSession interface are considered to be direct database
row-level operations, which result in immediate execution of a SQL
INSERT, UPDATE or DELETE respectively. Thus, they have very different
semantics to the Save(), SaveOrUpdate() and Delete() operations
defined by the ISession interface.