Using the same service & DAO from two threads - java

I've made a very simple REST controller method with Spring 3.0.0.RC1 that uses hibernate to perform a query. The query takes about ten seconds to complete. I've made this with intent so that I can fire up two requests to my controller.
Then I fire up the two requests, and query in MySQL (my DB backend) "show full processlist", and to my big surprise, there is only one request going on. One request will succeed, one request will fail with with the exception "org.hibernate.SessionException: Session is closed!" If I do more than two requests, only one will succeed, the others will fail in the same way. And there will always be just one query at a time, even though there should be multiple.
How can this be? Any suggestions?
To tell you a bit about my configuration, here is configuration that I use for the controller:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://127.0.0.1:3306/MyDb" />
<property name="username" value="angua" />
<property name="password" value="vonU" />
<property name="initialSize" value="2" />
<property name="maxActive" value="5" />
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="annotatedClasses">
<list>
<value>tld.mydomain.sample.entities.User</value>
<value>tld.mydomain.sample.entities.Role</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">false</prop>
</props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
<bean name="openSessionInViewInterceptor" class="org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor">
<property name="sessionFactory" ref="sessionFactory"/>
<property name="flushMode" value="0" />
</bean>
<bean id="txProxyTemplate" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" abstract="true">
<property name="transactionManager" ref="transactionManager"/>
<property name="transactionAttributes">
<props>
<prop key="create*">PROPAGATION_REQUIRED</prop>
<prop key="update*">PROPAGATION_REQUIRED</prop>
<prop key="delete*">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_SUPPORTS,readOnly</prop>
</props>
</property>
</bean>
<bean id="userService" parent="txProxyTemplate">
<property name="target">
<bean class="tld.mydomain.business.UserServiceImpl"/>
</property>
<property name="proxyInterfaces" value="tld.mydomain.business.UserService"/>
</bean>
<context:component-scan base-package="tld.mydomain"/>
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="interceptors">
<list>
<ref bean="openSessionInViewInterceptor" />
</list>
</property>
</bean>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="" p:suffix=".jsp"/>
<bean name="jsonView" class="org.springframework.web.servlet.view.json.JsonView">
<property name="encoding" value="ISO-8859-1"/>
<property name="contentType" value="application/json"/>
</bean>
and finally my controller code:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.json.JsonView;
import tld.mydomain.sample.business.UserService;
#Controller
#RequestMapping("/exp/*")
public class ExperimentsController {
#Autowired
private UserService userService;
#Autowired
private JsonView jsonView;
#RequestMapping(value="/long", method = RequestMethod.GET)
public ModelAndView lang() {
ModelAndView mav = new ModelAndView(jsonView);
userService.longQuery("UserA");
userService.longQuery("UserB");
return mav;
}
}
UPDATE: Here is UserServiceImpl
public class UserServiceImpl extends AbstractCRUDServiceImpl<User, String> {
#SuppressWarnings("unchecked")
#Override
public List<User> longQuery(String username) {
String like = "0" + username + "-%";
return DAO.getSession().createCriteria(User.class).setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY).addOrder(Order.asc("name"))
.createCriteria("interests").add(Restrictions.like("userPrefixedId", like))
.createCriteria("community").add(Restrictions.like("userPrefixedAuthorId", like))
.createCriteria("member").add(Restrictions.like("userPrefixedGroupId", like))
.add(Restrictions.isNotEmpty("skills"))
.list();
}
}
(The query is intentionally made slow so that I could easily reproduce the error for having multiple requests running at the same time and seeing how many simultaneous queries were running in the database)
And you'll need my AbstractCRUDServiceImpl and GenericCRUDDAO as well:
public abstract class AbstractCRUDServiceImpl<Entity extends PublishableEntity, PkID extends Serializable> implements CRUDService<Entity, PkID> {
protected GenericCRUDDAO<Entity, PkID> DAO = new GenericCRUDDAO<Entity, PkID>(dataType());
#Override
public void create(Entity entity) {
DAO.create(entity);
}
#Override
public void delete(Entity entity) {
DAO.create(entity);
}
#Override
public Entity read(PkID entityPk) {
return DAO.read(entityPk);
}
#Override
public void update(Entity entity) {
DAO.update(entity);
}
private Class<PkID> pkType = null;
#SuppressWarnings("unchecked")
public Class<PkID> pkType() {
if(pkType != null)
return pkType;
// Backup solution in case datatype hasn't been set
Type type = getClass().getGenericSuperclass();
if (type instanceof ParameterizedType) {
ParameterizedType paramType = (ParameterizedType) type;
pkType = (Class<PkID>) paramType.getActualTypeArguments()[1];
} else if (type instanceof Class) {
pkType = (Class<PkID>) type;
}
return pkType;
}
private Class<Entity> dataType = null;
#SuppressWarnings("unchecked")
private Class<Entity> dataType() {
if(dataType != null)
return dataType;
// Backup solution in case datatype hasn't been set
Type type = getClass().getGenericSuperclass();
if (type instanceof ParameterizedType) {
ParameterizedType paramType = (ParameterizedType) type;
dataType = (Class<Entity>) paramType.getActualTypeArguments()[0];
} else if (type instanceof Class) {
dataType = (Class<Entity>) type;
}
return dataType;
}
}
In GenericCRUDDAO, PublishableEntity is where all my entities descend from. It has a few simple convenience-methods such as checking if the entity is valid and what parts of it should be published vs kept to itself when used in a toString or similar
public class GenericCRUDDAO<EntityType extends PublishableEntity, PkID extends Serializable> implements CRUDDAO<EntityType, PkID> {
public GenericCRUDDAO() {}
public GenericCRUDDAO(Class<EntityType> datatype) {
this.setDataType(datatype);
}
private static SessionFactory sessionFactory = null;
public void setSessionFactory(SessionFactory sf) {
System.err.println("Setting SessionFactory for class " + this.getClass().getName());
sessionFactory = sf;
}
private Session session = null;
public Session getSession() {
if(session != null) {
if(session.isOpen())
return session;
}
if(sessionFactory == null)
Util.logError("sessionFactory is null");
session = ((SessionFactory) sessionFactory).getCurrentSession();
return session;
}
public void create(EntityType entity)
{
getSession().save(entity);
}
#SuppressWarnings("unchecked")
public EntityType read(PkID id)
{
return (EntityType) getSession().get(dataType(), id);
}
public void update(EntityType entity)
{
getSession().update(entity);
}
public void delete(EntityType entity) {
getSession().delete(entity);
}
public void delete(PkID id)
{
EntityType entity = read(id);
getSession().delete(entity);
}
private Class<EntityType> dataType = null;
#SuppressWarnings("unchecked")
private Class<EntityType> dataType() {
if(dataType != null)
return dataType;
// Backup solution in case datatype hasn't been set
Type type = getClass().getGenericSuperclass();
if (type instanceof ParameterizedType) {
ParameterizedType paramType = (ParameterizedType) type;
dataType = (Class<EntityType>) paramType.getActualTypeArguments()[0];
} else if (type instanceof Class) {
dataType = (Class<EntityType>) type;
}
return dataType;
}
public void setDataType(Class<EntityType> datatype) {
this.dataType = datatype;
}
}
I hope the configuration and code make it obvious why I only seem to be able to do one query at a time without them going into one-anothers feet.
Cheers
Nik

Looks pretty standard to me.
This of course assumes that:
JsonView is thread safe - which I think it is
Your implementation of UserService is also thread safe
Normally with this style of service singleton they are unless you have done something like keeping state in a member of UserServiceImpl
From what I can see in GenericCRUDDAO I'd pay close attention to the member session. If GenericCRUDDAO are singleton (one per domain object by the look of it) then you are going to run in to a bit of bother there.
The implementation of getSession() could actually be shorted to:
public Session getSession() {
return ((SessionFactory) sessionFactory).getCurrentSession();
}
This should be thread safe, assuming that the sessionFactory is using thread local sessions.

After having written my update, I've been looking at the same code over and over and over again, until it hit me what I kept looking at:
private Session session = null;
public Session getSession() {
if(session != null) {
if(session.isOpen())
return session;
}
if(sessionFactory == null)
Util.logError("sessionFactory is null");
session = ((SessionFactory) sessionFactory).getCurrentSession();
return session;
}
Since the service is a singleton, and it inherits from AbstractCRUDSerivceImpl, that news a DAO, "private Session session" in effect becomes a static instance. And "if(session.isOpen()) return session;" becomes a race condition. I've reduced the function now to:
public Session getSession() {
return ((SessionFactory) sessionFactory).getCurrentSession();
}
This seems to solve my problem. Does this look like a solution to you, or do I still have other glaring issues?
Cheers
Nik

Related

Apache Ignite mongo configuration using spring

I am introducing Apache Ignite in our application as cache system as well as for computation. I have configured spring application using following configuration class.
#Configuration
#EnableCaching
public class IgniteConfig {
#Value("${ignite.config.path}")
private String ignitePath;
#Bean(name="cacheManager")
public SpringCacheManager cacheManager(){
SpringCacheManager springCacheManager = new SpringCacheManager();
springCacheManager.setConfigurationPath(ignitePath);
return springCacheManager;
}
}
Using it like
#Override
#Cacheable("cache1")
public List<Channel> getAllChannels(){
List<Channel> list = new ArrayList<Channel>();
Channel c1 = new Channel("1",1);
Channel c2 = new Channel("2",2);
Channel c3 = new Channel("3",3);
Channel c4 = new Channel("4",4);
list.add(c1);
list.add(c2);
list.add(c3);
list.add(c4);
return list;
}
Now I want to add write-through and read-through feature. I could not find any documentation to connect ignite to mongo.
The idea is not to talk to db directly but through ignite using write behind feature.
EDIT-----------------------
As suggested I implemented
public class ChannelCacheStore extends CacheStoreAdapter<Long, Channel> implements Serializable {
#Override
public Channel load(Long key) throws CacheLoaderException {
return getChannelDao().findOne(Channel.mongoChannelCode, key);
}
#Override
public void write(Cache.Entry<? extends Long, ? extends Channel> entry) throws CacheWriterException {
getChannelDao().save(entry.getValue());
}
#Override
public void delete(Object key) throws CacheWriterException {
throw new UnsupportedOperationException("Delete not supported");
}
private ChannelDao getChannelDao(){
return SpringContextUtil.getApplicationContext().getBean(ChannelDao.class);
}
}
And added this CacheStore into cache configuration like below :
<property name="cacheConfiguration">
<list>
<bean class="org.apache.ignite.configuration.CacheConfiguration">
<property name="name" value="channelCache"/>
<property name="cacheMode" value="PARTITIONED"/>
<property name="atomicityMode" value="ATOMIC"/>
<property name="backups" value="1"/>
<property name="readThrough" value="true"/>
<!-- Sets flag indicating whether write to database is enabled. -->
<property name="writeThrough" value="true"/>
<!-- Enable database batching. -->
<!-- Sets flag indicating whether write-behind is enabled. -->
<property name="writeBehindEnabled" value="true"/>
<property name="cacheStoreFactory">
<bean class="javax.cache.configuration.FactoryBuilder$SingletonFactory">
<constructor-arg>
<bean class="in.per.amt.ignite.cache.ChannelCacheStore"></bean>
</constructor-arg>
</bean>
</property>
</bean>
</list>
</property>
But now getting class cast exception
java.lang.ClassCastException: org.springframework.cache.interceptor.SimpleKey cannot be cast to java.lang.Long
at in.per.amt.ignite.cache.ChannelCacheStore.load(ChannelCacheStore.java:19)
You can have any kind of backing database by implementing CacheStore interface:
https://apacheignite.readme.io/docs/persistent-store
Have you tried setting your key generator?
#CacheConfig(cacheNames = "cache1",keyGenerator = "simpleKeyGenerator")
https://github.com/spring-projects/spring-boot/issues/3625
So in the below line of code from what you have shared,
#Cacheable("cache1")
public List<Channel> getAllChannels(){
the #Cacheable annotation is being used on a method which is not accepting any parameters. Spring cache uses the parameters (if in basic data type) as a key for the cache (response obj as the value). I believe this makes the caching ineffective.

Hibernate 4 persist() works but merge() does not

I am writing a webapp that runs on Wildfly 8.2 and uses Hibernate 4. I've gotten it to successfully persist a new entity but cannot seem to make it commit changes to it afterwards. I'm kind of assumming it's some transaction setting that I have wrong, but I'm not sure what it is. I have a service layer, in which I set the transaction settings and that layer calls a DAO layer. Here's an example:
#Transactional
#EnableTransactionManagement
#TransactionManagement(value = TransactionManagementType.CONTAINER)
#TransactionAttribute(value = TransactionAttributeType.REQUIRED)
#Stateless
#Interceptors(SpringBeanAutowiringInterceptor.class)
#DeclareRoles("Security Admin")
public class SecurityServiceBean
{
#Override
#PermitAll
public UserRegistration confirmRegistration(
String confirmationCode) throws ApplicationException
{
try
{
QueryResults<UserRegistration> userRegistrations = this.userRegistrationDAO
.find(new UserRegistrationQuery(null, confirmationCode));
if (userRegistrations.getTotalRecords() == 1)
{
UserRegistration userRegistration = userRegistrations.uniqueResult();
if (userRegistration.getConfirmationDate() == null)
{
userRegistration.setConfirmationDate(new Date());
userRegistration.setState(State.CONFIRMED);
userRegistration = this.userRegistrationDAO.saveOrUpdate(userRegistration);
...
}
...
}
}
}
}
and the base DAO class has this
public abstract class AbstractJpaDataAccessObject implements DataAccessObject
{
public <T extends UniqueObject<?>> T saveOrUpdate(
T obj) throws DAOException
{
try
{
if (obj.getId() == null)
{
this.em.persist(obj);
}
else
{
T attached = this.em.merge(obj);
this.em.flush();
return attached;
}
return obj;
}
catch (PersistenceException e)
{
throw new DAOException("[saveOrUpdate] obj=" + obj.toString() + ",msg=" + e.getMessage(), e);
}
}
}
I know that I should not need the call to flush(), but I wanted to try it to see if that helped, which it did not.
So what am I missing?
UPDATE:
There is no exception being thrown. The object being returned from SecurityServiceBean.confirmRegistration() has all of the changes that were made in the method. However, querying the database shows that the changes were not committed. None of my fields in the entity are marked as updateable=false. Below is an example. I'll limit the fields to just the "status" field which is one of the fields I expect to be updated.
#Entity
#Table(name = "user_registrations", schema = "campaigner")
public class UserRegistration extends AbstractUserRegistration
{
}
and the mapped superclass.
#MappedSuperclass
public class AbstractUserRegistration extends CampaignerHistoryObject<Long>
{
public static enum State {
UNCONFIRMED, CONFIRMED, APPROVED, DENIED,
};
private State state;
#Column(name = "STATE")
public State getState()
{
return state;
}
}
And here's two XML files that I use. The first is beanRefContent.xml
<beans>
<!-- <aop:aspectj-autoproxy proxy-target-class="true"/> -->
<!--
SpringBeanAutowiringInterceptor needs this file.
We need SpringBeanAutowiringInterceptor to autowire the EJBs.
-->
<bean
class="org.springframework.context.support.ClassPathXmlApplicationContext">
<constructor-arg value="classpath:campaignerContext.xml" />
</bean>
</beans>
The second is campaignerContext.xml.
<beans>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="campaigner" />
</bean>
<bean id="em" class="org.springframework.orm.jpa.support.SharedEntityManagerBean">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
</beans>
UPDATE 2: Now I'm starting to think that my problems lie in my persistence.xml file, shown below:
<persistence>
<persistence-unit name="campaigner" transaction-type="JTA">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<jta-data-source>java:/jdbc/CampaignerDS</jta-data-source>
...
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLInnoDBDialect" />
<property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform" />
<property name="jta.UserTransaction" value="java:jboss/UserTransaction" />
<property name="jta.TransactionManager" value="java:jboss/TransactionManager" />
</properties>
</persistence-unit>
</persistence>

How to avoid hardcoding a string in java file?

I am using spring. I need to return object based on the String. I have below code.
public class DaoFactoryImpl implements DaoFactory {
private String dbType;
private OrganizationActions organizationActions;
private ProductActions productActions;
public void setOrganizationActions(OrganizationActions org){
this.organizationActions = org;
}
public void setProductActions(ProductActions prodActions){
this.productActions = prodActions;
}
public void setDbType(String dbType){
this.dbType = dbType;
}
#Override
public OrganizationActions getDaoObject() {
if(dbType.equalsIgnoreCase("Oracle")){
return organizationActions;
}else if(dbType.equalsIgnoreCase("DB2")){
return productActions;
}
return null;
}
}
Spring_congig.xml:
<util:properties id="configProps"
location="classpath:config/config.properties" />
<bean id="orgService" class="com.sample.OrganizationMongoService">
</bean>
<bean id="productService" class="com.sample.ProductMongoService"/>
<bean id="daoFactory" class="com.sample.factory.DaoFactoryImpl">
<property name="dbType" value="${dbName}"/>
<property name="organizationActions" ref="orgService"/>
<property name="productActions" ref="productService"/>
</bean>
I specify dbName in config.properties file. I have hard coded the same dbName (Oracle, DB2) in DaoFactoryImpl class. How can I avoid hard coding Oracle, DB2 in the code. Is there anyway to specify this criteria in the spring xml file?
Try creating a map in your spring config and use it to look up the correct instance. For example:
<bean id="daoFactory" class="com.sample.factory.DaoFactoryImpl">
<property name="dbType" value="${dbName}"/>
<property name="typeMap">
<map>
<entry key="Oracle" value-ref="orgService"/>
<entry key="DB2" value-ref="productService"/>
</map>
<property>
</bean>
Then do a lookup in your factory method:
public void setTypeMap(Map<String,Actions> typeMap){
this.typeMap = typeMap;
}
#Override
public OrganizationActions getDaoObject() {
return typeMap.get(dbType);
}
You can add the below code in Spring_congig.xml:-
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>properties/database.properties</value>
</property>
</bean>
and define your key-value pair in database.properties as:-
dbName=Oracle
Your Spring_congig.xml will pick up the required value for the given key.
<property name="dbType" value="${dbName}"/>

How to implement batch operations with MyBatis/Spring?

I am wondering how to implement batch operations with my insert statements using MyBatis 3 & Spring 3?
For example, here is what is currently being done:
spring.xml:
<bean id="jndiTemplateDatasource" class="org.springframework.jndi.JndiTemplate">
<property name="environment">
<props>
<prop key="java.naming.factory.initial">${context.factory}</prop>
</props>
</property>
</bean>
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiTemplate" ref="jndiTemplateDatasource"/>
<property name="jndiName" value="${connectionpool.jndi}"/>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.test" />
</bean>
MyService.xml:
<insert id="insertMyRecord" parameterType="com.test.MyRecord" >
insert into ... // code removed
</insert>
MyService.java:
public interface MyService {
public void insertMyRecord (MyRecord);
}
MyController.java:
#Controller
public class MyController {
#Autowired
private MyService myService;
#Transactional
#RequestMapping( .... )
public void bulkUpload (#RequestBody List<MyRecord> myRecords) {
for (MyRecord record : myRecords) {
myService.insertMyRecord(record);
}
}
}
Disclaimer: That is just pseudo code for demonstration purposes
So what can I do to turn that into a batch process?
Ideally I want to be able to do it with least "intrusion" into code, i.e. use annotations more preferred, but if not possible what is the next best thing?
Also, this needs to be configured just for this one service, not for everything in the project.
The accepted answer above doesn't actually get you batch mode for MyBatis. You need to choose the proper Executor via ExecutorType.BATCH. That is either passed as a parameter to SqlSession.openSession in standard MyBatis API or, if using MyBatis-Spring, as an option to the SqlSessionTemplate. That is done via:
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory" />
<constructor-arg index="1" value="BATCH" />
</bean>
There is nothing else that needs to be done.
This is running and tested example ...
Update multiple rows using batch (ibatis + java )
In this ex. I am updating attending count from table with respective to partyid.
public static int updateBatch(List<MyModel> attendingUsrList) {
SqlSession session = ConnectionBuilderAction.getSqlSession();
PartyDao partyDao = session.getMapper(PartyDao.class);
try {
if (attendingUsrList.size() > 0) {
partyDao.updateAttendingCountForParties(attendingUsrList);
}
session.commit();
} catch (Throwable t) {
session.rollback();
logger.error("Exception occurred during updateBatch : ", t);
throw new PersistenceException(t);
} finally {
session.close();
}
}
Model class where variable is defined :
public class MyModel {
private long attending_count;
private String eid;
public String getEid() {
return eid;
}
public void setEid(String eid) {
this.eid = eid;
}
public long getAttending_count() {
return attending_count;
}
public void setAttending_count(long attending_count) {
this.attending_count = attending_count;
}
}
party.xml code
Actual query where batch execute
<foreach collection="attendingUsrList" item="model" separator=";">
UPDATE parties SET attending_user_count = #{model.attending_count}
WHERE fb_party_id = #{model.eid}
</foreach>
Interface code here
public interface PartyDao {
int updateAttendingCountForParties (#Param("attendingUsrList") List<FBEventModel>attendingUsrList);
}
Here is my batch session code
public static synchronized SqlSession getSqlBatchSession() {
ConnectionBuilderAction connection = new ConnectionBuilderAction();
sf = connection.getConnection();
SqlSession session = sf.openSession(ExecutorType.BATCH);
return session;
}
SqlSession session = ConnectionBuilderAction.getSqlSession();
I'm not sure I understand the question fully correct but I will try to give you my thoughts.
For making the single service I would recommend to generify the service interface:
public void bulkUpload (#RequestBody List<T> myRecords)
Then you can check the type of the object and call the propper mapper repository.
Then you can generify it more by creating a common interface:
public interface Creator<T> {
void create(T object);
}
and extend it by your mapper interface:
public interface MyService extends Creator<MyRecord>{}
Now the most complicated step: you get the object of a particular type, see what exact mapper implements the Creator interface for this class (using java reflection API) and invoke the particular method.
Now I give you the code I use in one of my projects:
package com.mydomain.repository;
//imports ...
import org.reflections.Reflections;
#Repository(value = "dao")
public class MyBatisDao {
private static final Reflections REFLECTIONS = new Reflections("com.mydomain");
#Autowired
public SqlSessionManager sqlSessionManager;
public void create(Object o) {
Creator creator = getSpecialMapper(Creator.class, o);
creator.create(o);
}
// other CRUD methods
#SuppressWarnings("unchecked")
private <T> T getSpecialMapper(Class<T> specialClass, Object parameterObject) {
Class parameterClass = parameterObject.getClass();
Class<T> mapperClass = getSubInterfaceParametrizedWith(specialClass, parameterClass);
return sqlSessionManager.getMapper(mapperClass);
}
private static <T, P> Class<? extends T> getSubInterfaceParametrizedWith(Class<T> superInterface, Class<P> parameterType) {
Set<Class<? extends T>> subInterfaces = REFLECTIONS.getSubTypesOf(superInterface);
for (Class<? extends T> subInterface: subInterfaces) {
for (Type genericInterface : subInterface.getGenericInterfaces()) {
if (!(genericInterface instanceof ParameterizedType)) continue;
ParameterizedType parameterizedType = (ParameterizedType) genericInterface;
Type rawType = parameterizedType.getRawType();
if (rawType instanceof Class<?> && ((Class<?>) rawType).isAssignableFrom(superInterface)) {
for (Type type: parameterizedType.getActualTypeArguments()) {
if (type instanceof Class<?> && ((Class<?>) type).isAssignableFrom(parameterType)) {
return subInterface;
}
}
}
}
}
throw new IllegalStateException(String.format("No extension of %s found for parametrized type %s ", superInterface, parameterType));
}
}
Warning! This approach can have bad performance impact so use it in non-performance-critical actions
If you want bulk insert I would recommend to use mybatis foreach for bulk insert as described here.
If you think you don't want to write sql for every type of objects you better use Hibernate or any other advanced ORM. MyBatis is just an SQL mapping interface.

How to wire Model View and Controller using Spring

In a swing GUI application, where MVC pattern is applied, how can we use Spring to wire the model view and controller? i.e. what beans (model, view or controller) should be injected using spring and what should be created from the application? I have applied the MVC pattern described here when developing the application. Thanks in advance.
If you have some leeway in the technologies you're using, I'd say you switch to (Griffon)[http://griffon.codehaus.org/]. It uses spring in the background and you also get the power of groovy and Swing UI builders. Best part is, you can still reuse the java code you've written so far. Also, you don't need to worry about DI and stuff. Griffon handles it for you.
On one of my projects, I successfully used Spring Rich Client.
If you are starting from scratch, I suggest that you take a look at it, it worth it. And it also provides some services out of the box (like authentication box and so).
I suggest that you can use "spring mvc".
Jsp(View) controller how to show the data;
Controller controll the return the view required data;
Server controller the system logic;
Model is the database model.
It would come to noone's surprise that I'd recommend you to have a look at Griffon. The MVC pattern is deeply engrained in Griffon's DNA, have a look at this sample app as shown in the Griffon Guide
http://griffon.codehaus.org/guide/0.9.5-rc2/guide/2.%20Getting%20Started.html#2.3%20A%20Groovy%20Console%20Example
Griffon provides basic DI capabilities for each MVC member, you only need to define properties following a naming convention. Services, where you would usually put most of the application's logic, are also automatically injected into controllers, as the guide explains in
http://griffon.codehaus.org/guide/0.9.5-rc2/guide/8.%20Controllers%20and%20Services.html#8.2%20Services
However you can make use of Spring DI too via the Spring plugin
http://artifacts.griffon-framework.org/plugin/spring
Spring beans may be defined using the standard XML approach, annotations or the Groovy Spring DSL.
I defined all the beans in spring and used a factory method to create the views when required. Controller is injected to the view and the model and view are added to the controller via spring.
Following are the code samples from a simple example that I came up with, in order to find a solution: (sorry for the long post!)
the application context file:
<bean id="firstModel" class="com.model.FirstModel"></bean>
<bean id="secondModel" class="com.model.SecondModel"></bean>
<bean id="firstController" class="com.controller.FirstController" />
<bean
class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject">
<ref local="firstController" />
</property>
<property name="targetMethod">
<value>addModel</value>
</property>
<property name="arguments">
<list>
<value>FIRST</value>
<ref local="firstModel" />
</list>
</property>
</bean>
<bean id="secondController" class="com.controller.SecondController" />
<bean
class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject">
<ref local="secondController" />
</property>
<property name="targetMethod">
<value>addModel</value>
</property>
<property name="arguments">
<list>
<value>SECOND</value>
<ref local="secondModel" />
</list>
</property>
</bean>
<bean
class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject">
<ref local="secondController" />
</property>
<property name="targetMethod">
<value>addModel</value>
</property>
<property name="arguments">
<list>
<value>FIRST</value>
<ref local="firstModel" />
</list>
</property>
</bean>
<bean id="firstForm" class="com.view.FirstForm">
<property name="controller">
<ref bean="firstController" />
</property>
</bean>
<bean id="secondForm" class="com.view.SecondForm">
<property name="controller">
<ref bean="secondController" />
</property>
</bean>
following is the abstract controller class:
public class AbstractController implements PropertyChangeListener {
Map<Type, BaseView> registeredViews;
Map<Type, AbstractModel> registeredModels;
public AbstractController() {
registeredViews = new HashMap<Type, BaseView>();
registeredModels = new HashMap<Type, AbstractModel>();
}
public void addModel(Type type, AbstractModel model) {
registeredModels.put(type, model);
model.addPropertyChangeListener(this);
}
public void removeModel(AbstractModel model) {
registeredModels.remove(model);
model.removePropertyChangeListener(this);
}
public void addView(BaseView view, Type type) {
registeredViews.put(type, view);
}
public void removeView(javax.swing.JFrame view) {
registeredViews.remove(view);
}
public void propertyChange(PropertyChangeEvent evt) {
for (BaseView view : registeredViews.values()) {
view.modelPropertyChange(evt);
}
}
protected void setModelProperty(String propertyName, Object newValue) {
for (AbstractModel model : registeredModels.values()) {
Statement statment = new Statement(model, "set" + propertyName, new Object[] { newValue });
try {
statment.execute();
} catch (NoSuchMethodException e) {
continue;
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
following is the abstract model class:
public class AbstractModel {
protected PropertyChangeSupport propertyChangeSupport;
public AbstractModel() {
propertyChangeSupport = new PropertyChangeSupport(this);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.removePropertyChangeListener(listener);
}
protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
propertyChangeSupport.firePropertyChange(propertyName, oldValue, newValue);
}
}
Following is the code sample of the view interface:
public interface BaseView {
void modelPropertyChange(PropertyChangeEvent evt);
public abstract void showForm();
}
following is the code sample of the factory class:
public class FormFactory {
private ApplicationContext context;
private static FormFactory viewFactory;
private FormFactory() {
if (context == null) {
context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
}
}
public static synchronized FormFactory getInstance() {
if (viewFactory == null) {
viewFactory = new FormFactory();
}
return viewFactory;
}
public BaseView createForm(Type type) {
BaseView form = null;
switch (type) {
case FIRST:
form = (BaseView) context.getBean("firstForm");
break;
case SECOND:
form = (BaseView) context.getBean("secondForm");
break;
default:
break;
}
return form;
}
}

Categories