Below is my controller. My program generates an output, based on a form input. Across the project, there are multiple input forms, that generate the output object. So, the essential flow is the same. So I want a single multi-action controller that does all of that.
Challenges:
1. The service classes change. Although all services implement the same interface, and controller calls the same interface method.
2. The input objects change. Although the input objects do not have any methods other than setters, and getters. So I let them all implement an empty interface.
Questions:
How do I change the qualifier, based on the path. Can I use path variables?
Suppose the path has this value -> singleton. Then my corresponding bean names would be singletonService and singletonInput. I want to make a constant class that has stores this mapping information. So, can I call that from inside the qualifier, using some Spring Expression Language? Example, instead of Qualifier(variablePathName) -> Qualifier(getQualifierName['variablePathName']) Something like that?
Please also clarify the theory behind this. From what I understand, beans are created, autowired before the Request are mapped... Does this mean that what I'm trying to achieve here is simply not possible. In that case, would you suggest making Controller-service pairs for handling each request, with basically the same code? But I feel there must be some way to achieve what I'm trying...
Code:
#Cotroller
#RequestMapping(value="/generate/{path}")
public class TestController {
#Autowired
#Qualifier(......)
private IService service;
#Autowired
#Qualifier(......)
IUserInput userInput;
#RequestMapping(method = RequestMethod.POST)
//Some handler method
}
You're right in that the autowiring is all done once up front (point 3). You wouldn't be able to achieve what you want using fields annotated #Autowired and #Qualifier - as these fields would always reference the same bean instance.
You may be better to ask Spring for the particular service bean by name - based on the path variable. You could do it within a single controller instance. For example:
#Cotroller
#RequestMapping(value="/generate/{path}")
public class TestController {
#Autowired
private ApplicationContext applicationContext;
#RequestMapping(method = RequestMethod.POST)
public String someHandlerMethod(#PathVariable String path) {
IService service = (IService) applicationContext.getBean(path + "Service");
IUserInput userInput = (IUserInput) applicationContext.getBean(path + "UserInput");
// Logic using path specific IService and IUserInput
}
}
Related
I'm trying a new development method.
In mybatis3, I write mapper.java and mapper.xml usually.
I know, the sql statements is corresponded by sqlId(namespace+id).
I want to execute the sql statement like this :
SqlSession sqlSession = sessionFactory.openSession();
return sqlSession.selectList(sqlId, param);
but I get a error:
Cause: java.lang.IllegalArgumentException: Mapped Statements collection does not contain value for mapper.JinBoot.test
at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:150)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:141)
at cn.tianyustudio.jinboot.dao.BaseDao.select(BaseDao.java:20)
at cn.tianyustudio.jinboot.service.BaseService.select(BaseService.java:10)
at cn.tianyustudio.jinboot.controller.BaseController.test(BaseController.java:21)
here is my BaseDao.java
public class BaseDao {
private static SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
public static List<Map> select(String sqlId, Map param) {
try {
factoryBean.setDataSource(new DruidDataSource());
SqlSessionFactory sessionFactory = factoryBean.getObject();
SqlSession sqlSession = sessionFactory.openSession();
return sqlSession.selectList(sqlId, param);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
here is UserMapper.xml
<mapper namespace="mapper.JinBoot">
<select id="test" parameterType="hashMap" resultType="hashMap">
select * from user
</select>
</mapper>
the application.properties
mybatis.mapperLocations=classpath:mapper/*.xml
I start the project, the send a http request, after controller and service ,the param 'sqlId' in BaseDao is 'mapper.JinBoot.test' (see error info).
In method 'BaseDao.select', both the parameter and the result type is Map.
So I don't want to create UserMapper.java, I want try it.
How can I resolve it? What's the malpractice of this way?
This does not work because spring boot creates its own SqlSessionFactory. And the option in application.properties that specifies where mappers should be looked for is only set for that SqlSessionFactory. You are creating unrelated session factory in your DAO and it does not know where to load mappers definition.
If you want to make it work you need that you DAO is spring managed so that you can inject mybatis session factory into it and use it in select. This would also require that you convert select into non static method.
As I understand you want to have only one method in you base DAO class and use it in individual specific DAO classes. I would say it makes little sense. If the method returns Map there will be some place that actually maps this generic type to some application specific types. This would probably be in the child DAOs. So you still need to create the API of the child DAO with the signature that uses some input parameters and returns some domain objects. And that's exactly what you want to avoid by not creating mybatis mapper classes.
The thing is that you can treat your mytabis mappers as DAOs. That is you mappers would be your DAOs. And you don't need another layer. As I understand now you have two separate layers - DAO and mappers and you want to remove boilerplate code. I think it is better to remove DAO classes. They are real boilerplate and mybatis mapper can serve as DAO perfectly. You inject it directly to you service and service depends only on the mapper class. The logic of the mapping is in the mapper xml file. See also answer to this question Can Spring DAO be merged into Service layer?
I'm trying to follow a tutorial on using the unit of work and repository pattern on MSDN but I've stumbled at the below:
private UnitOfWork unitOfWork = new UnitOfWork();
"This code adds a class variable for the UnitOfWork class. (If you were using interfaces here, you wouldn't initialize the variable here; instead, you'd implement a pattern of two constructors"
Basically, I need to call my UnitOfWork class in my LogController without actually using the above code as I'm using an interface? How is this possible? I'm not sure what it means by 'two constructors'.
Any advice would be appreiciated
In your class you define
Private IUnitOfWork _unitOfWork = null;
And you have one constructor which accepts an IUnitOfWork so the caller can pass in an implementation:
Public MyClass (IUnitOfWork unitOfWork) {
_unitOfWork = unitOfWork;
}
And you have another which does not, but knows how to go and find which implementation to create. Either it can use a default implementation, or it can go to some config file you've written to define which type to use, etc.
Within the MyClass you still call _unitOfWork.whateverMethod()
I am learning asp.net mvc3. one example I found online is to show me how to use IOC.
public class HomeController : Controller
{
private IHelloService _service;
public HomeController():this(new HelloService())
{}
public HomeController(IHelloService service)
{
_service = service;
}
}
there are two constructors in this example. I understand the second one. the first one I understand what that for, but to me, it seems like extra code, you will never need it.
can someone please explain to me whats the point to add the first constructor.
public HomeController():this(new HelloService())
{}
When the MVC Framework instantiates a controller, it uses the default (parameter-less) constructor.
By default, you are injecting a concrete IHelloService implementation. This will be used when a user navigates to the action.
Unit Tests would use the overload and pass in the mock IHelloService implementation rather than calling the default constructor.
It can be useful if you don't use a dependency injection framework that injects this for you. In this way you never have to manually inject the object, the object will handle that by itself.
The second constructor is, of course, useful to inject custom objects when unit testing.
Normally one would need to do this:
IFoo foo = new Foo();
IBar bar = new Bar(foo);
When your constructor creates a default object you can just do this:
IBar bar = new Bar();
Bar will then create a Foo and inject it into itself.
Consider a class (an ORM entity):
public class MyEntity {
Long id;
MyOtherEntity assoc;
// ... getters and setters
}
I want for it to be bound automatically in a Spring MVC controller, something like that:
public ModelAndView method(HttpServletRequest request,
HttpServletResponse response, MyEntity command) {
}
It works well for simple properties like id, but for assoc it throws an exception NullValueInNestedPathException, since assoc wasn't instantiated by constructor. The question is, how can I tell ServletRequestDataBinder (or BeanWrapper or anything) to instantiate properties automatically as it makes its way through the nested property path?
I could of course make another class derived from MyEntity and put an instantiation in it, but then I won't be able to save it using simple Hibernate call, since derived class won't be mapped.
Ok, one way to do that in case of MultiActionController is to override newCommandObject; but I would like a more generic solution.
I have a form where I have two fields that I can add as much as I can. Think of it as like the upload file in gmail where I can add 1,2,3... files to upload only that I have two fields.
I am not so sure how this will check out using a SimpleFormController in Spring. Will the Spring Controller bind the them automatically?
My command class looks like this:
public class Course {
private long ID;
private String Owner;
private String Title;
private String Learning Objective;
//I am not so sure how this will be bound
private List<LearningActivity> learningActivities;
//accessor methods
}
public class LearningActivity {
private String Description;
private String link;
//accessor methods
}
I would suggest you to use Annotation-based Spring controllers as SimpleFormController is deprecated as of spring 3.0
If you are using annotations based
controller then their is no need to
extend any class or implement any
interface. The only thing you need to
do to make your simple java class to
become a Spring controller is to add
the #Controller annotation to it.
Example here
Also for handling dynamic fields in the form, it is better that you use Spring Form Tags
Example here
Edit: check 5.4.2.1. Registering additional custom PropertyEditors in spring docs, it has an example of what u want