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;
}
}
Related
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.
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}"/>
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.
We're in the process of updating our apps from Spring 2.5 to 3.0 and we've hit a problem with the new SpEL evaluation of bean properties.
We've been using an in-house templating syntax in one module which unfortunately uses the same "#{xyz}" markup as SpEL. We have a few beans which take string's containing these expressions as properties but spring assumes they are SpEL expressions and throws a SpelEvaluationException when it tries to instantiate the bean.
e.g.
<bean id="templatingEngine" class="com.foo.TemplatingEngine">
<property name="barTemplate" value="user=#{uid}&country=#{cty}"/>
</bean>
Is it possible to disable SpEL evaluation, ideally per-bean, but alternatively for the whole application context?
Alternatively is there a way to escape the values?
Thanks,
Stephen
Completely disable SpEL evaluation by calling the bean factory setBeanExpressionResolver method passing in null. You can define a BeanFactoryPostProcessor to do this.
public class DisableSpel implements BeanFactoryPostProcessor {
public void postProcessBeanFactory(
ConfigurableListableBeanFactory beanFactory)
throws BeansException
{
beanFactory.setBeanExpressionResolver(null);
}
}
Then define this bean in the application context.
<bean class="com.example.spel.DisableSpel"/>
Well what you could do is re-define the expression language delimiters.
I would say the way to do this is through a special bean that implements BeanFactoryPostProcessor (thanks to inspiration by Jim Huang):
public class ExpressionTokensRedefiner implements BeanFactoryPostProcessor{
private BeanExpressionResolver beanExpressionResolver;
public void setBeanExpressionResolver(
final BeanExpressionResolver beanExpressionResolver){
this.beanExpressionResolver = beanExpressionResolver;
}
#Override
public void postProcessBeanFactory(
final ConfigurableListableBeanFactory beanFactory)
throws BeansException{
beanFactory.setBeanExpressionResolver(createResolver());
}
private String expressionPrefix = "${";
private String expressionSuffix = "}";
public void setExpressionPrefix(final String expressionPrefix){
this.expressionPrefix = expressionPrefix;
}
public void setExpressionSuffix(final String expressionSuffix){
this.expressionSuffix = expressionSuffix;
}
private BeanExpressionResolver createResolver(){
if(beanExpressionResolver == null){
final StandardBeanExpressionResolver resolver =
new StandardBeanExpressionResolver();
resolver.setExpressionPrefix(expressionPrefix);
resolver.setExpressionSuffix(expressionSuffix);
return resolver;
} else{
return beanExpressionResolver;
}
}
}
Define it as a bean like this:
<bean class="foo.bar.ExpressionTokensRedefiner">
<property name="expressionPrefix" value="[[" />
<property name="expressionSuffix" value="]]" />
</bean>
or like this:
<!-- this will use the default tokens ${ and } -->
<bean class="foo.bar.ExpressionTokensRedefiner" />
or use a custom resolver:
<bean class="foo.bar.ExpressionTokensRedefiner">
<property name="beanExpressionResolver">
<bean class="foo.bar.CustomExpressionResolver" />
</property>
</bean>
Now you can leave your definitions untouched and if you want to use SpEL, use the new delimiters.
EDIT: now I did test it and it actually works.
<bean class="foo.bar.ExpressionTokensRedefiner">
<property name="expressionPrefix" value="[[" />
<property name="expressionSuffix" value="]]" />
</bean>
<bean class="foo.bar.FooFritz">
<property name="fizz" value="[[ systemProperties['user.home'] ]]"></property>
<property name="fozz" value="[[ systemProperties['java.io.tmpdir'] ]]"></property>
<!-- this is what it would normally choke on -->
<property name="fazz" value="#{ boom() }"></property>
</bean>
Test code:
final ConfigurableApplicationContext context =
new ClassPathXmlApplicationContext("classpath:foo/bar/ctx.xml");
context.refresh();
final FooFritz fooFritz = context.getBean(FooFritz.class);
System.out.println(fooFritz.getFizz());
System.out.println(fooFritz.getFozz());
System.out.println(fooFritz.getFazz());
Output:
/home/seanizer
/tmp
#{ boom() }
I am not a dab, but this mighbe of help.
https://issues.apache.org/jira/browse/CAMEL-2599
I'd like to convert this SimpleFormController to use the annotation support introduced in Spring MVC 2.5
Java
public class PriceIncreaseFormController extends SimpleFormController {
ProductManager productManager = new ProductManager();
#Override
public ModelAndView onSubmit(Object command)
throws ServletException {
int increase = ((PriceIncrease) command).getPercentage();
productManager.increasePrice(increase);
return new ModelAndView(new RedirectView(getSuccessView()));
}
#Override
protected Object formBackingObject(HttpServletRequest request)
throws ServletException {
PriceIncrease priceIncrease = new PriceIncrease();
priceIncrease.setPercentage(20);
return priceIncrease;
}
}
Spring Config
<!-- Include basic annotation support -->
<context:annotation-config/>
<!-- Comma-separated list of packages to search for annotated controllers. Append '.*' to search all sub-packages -->
<context:component-scan base-package="springapp.web"/>
<!-- Enables use of annotations on controller methods to map URLs to methods and request params to method arguments -->
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>
<bean name="/priceincrease.htm" class="springapp.web.PriceIncreaseFormController">
<property name="sessionForm" value="true"/>
<property name="commandName" value="priceIncrease"/>
<property name="commandClass" value="springapp.service.PriceIncrease"/>
<property name="validator">
<bean class="springapp.service.PriceIncreaseValidator"/>
</property>
<property name="formView" value="priceincrease"/>
<property name="successView" value="hello.htm"/>
<property name="productManager" ref="productManager"/>
</bean>
Basically, I'd like to replace all the XML configuration for the /priceincrease.htm bean with annotations within the Java class. Is this possible, and if so, what are the corresponding annotations that I should use?
Thanks,
Don
It'll look something like the following, although whether it works or not exactly as is will depend a bit on your configuration (view resolver, etc). I should also note that there are about eight billion valid ways to write this thing. See the Spring documentation, 13.11.4 "Supported handler method arguments and return types" for an overview of the insanity. Also note that you can autowire the validator
#Controller
#RequestMapping("/priceincrease.htm")
public class PriceIncreaseFormController {
ProductManager productManager;
#Autowired
public PriceIncreaseFormController(ProductManager productManager) {
this.productManager = productManager;
}
// note: this method does not have to be called onSubmit
#RequestMapping(method = RequestMethod.POST)
public String onSubmit(#ModelAttribute("priceIncrease") PriceIncrease priceIncrease, BindingResult result, SessionStatus status {
new PriceIncreaseValidator().validate(priceIncrease, result);
if (result.hasErrors()) {
return "priceincrease";
}
else {
int increase = priceIncrease.getPercentage();
productManager.increasePrice(increase);
status.setComplete();
return "redirect:hello.htm";
}
}
// note: this method does not have to be called setupForm
#RequestMapping(method = RequestMethod.GET)
public String setupForm(Model model) {
PriceIncrease priceIncrease = new PriceIncrease();
priceIncrease.setPercentage(20);
model.addAttribute("priceIncrease", priceIncrease);
return "priceincrease";
}
}
Someone completed this project with a recent MVC and it's on github, so you can see how all the classes are changed compared to Spring's tutorial.
Link: PriceIncreaseFormController