Spring MVC - The #Controller annotation does not get autodetected - spring

I am new in spring, therefore maybe my question is stupid! I am trying to add a controller to Spring's Petclinic example. However, the mappings in this controller do not work and I get 404 error. I added the same mapping to the other existing controllers and it works. I want to see what I am doing wrong. Here is my controller:
package org.springframework.samples.petclinic.web;
import java.util.Map;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.stereotype.Controller;
#Controller
public class TwitterController {
#RequestMapping(value = "/twitter/searchtweets", method = RequestMethod.GET)
public String initFindForm(Map<String, Object> model) {
...
return "twitter/searchtweets";
}
}
and here is the component scan definition in my MVC config file:
<context:component-scan
base-package="org.springframework.samples.petclinic.web"/>
And:
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="contentNegotiationManager" ref="cnManager"/>
<property name="viewResolvers">
<list>
<!-- Default viewClass: JSTL view (JSP with html output) -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- Example: a logical view name of 'vets' is mapped to '/WEB-INF/jsp/vets.jsp' -->
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!-- Used here for 'xml' and 'atom' views -->
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver"/>
</list>
</property>
</bean>
<!-- Simple strategy: only path extension is taken into account -->
<bean id="cnManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="favorPathExtension" value="true"/>
<property name="ignoreAcceptHeader" value="true"/>
<property name="defaultContentType" value="text/html"/>
<property name="mediaTypes">
<map>
<entry key="html" value="text/html" />
<entry key="xml" value="application/xml" />
<entry key="atom" value="application/atom+xml" />
</map>
</property>
</bean>
The address: localhost:8081/petclinic/twitter/searchtweets.html returns 404 Error. It's worth mentioning that the jsp file here in: webapp/WEB_INF/jsp/twitter/searchtweets.jsp
Am I missing something?

Related

How to provide reference bean to XmlViewResolver

i am generating Excel Sheet using Spring MVC , for this reason i have the following in my appcontext.xml file
appcontext.xml
<bean class="org.springframework.web.servlet.view.XmlViewResolver">
<property name="order" value="1"/>
<property name="location" value="/WEB-INF/views.xml"/>
</bean>
views.xml
<bean id="excelView" class="com.xxxx.xx.util.MyListExcelView" />
​
Instead of having the bean reference excelView in a separate views.xml file .can we mention the below in the same appcontext.xml
<bean id="excelView" class="com.xxxx.xx.util.MyListExcelView" />
Sure, you can.
Just add BeanNameViewResolver to your ViewResolver chain.
Of course, you should remove excelView from views.xml in this case.
Here is an example:
appcontext.xml:
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver">
<property name="order" value="0"/>
</bean>
<bean class="org.springframework.web.servlet.view.XmlViewResolver">
<property name="order" value="1"/>
<property name="location" value="/WEB-INF/views.xml"/>
</bean>
<bean id="excelView" class="com.xxxx.xx.util.MyListExcelView" />
YourController.java
#Controller
public class YourController {
// ...
#RequestMapping(value = "/test")
public String test() {
// populate your model here
return "excelView";
}
// ...
}

Spring data Truncate TransactionRequiredException Executing an update/delete query

Repo defined as below:
import javax.transaction.Transactional;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import com.thy.crewdb.jpa.Schedule;
public interface ScheduleRepo extends CrudRepository<Schedule, String> {
#Query(value = "TRUNCATE TABLE SCHEDULE", nativeQuery = true)
#Modifying
#Transactional
void truncate();
}
Application context defined as below :
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="xJndi" />
<property name="resourceRef" value="true" />
</bean>
<!-- ################################################################################################################ -->
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="packagesToScan" value="com.x.jpa" />
<property name="jtaDataSource" ref="dataSource" />
<property name="validationMode" value="NONE" />
<property name="jpaDialect">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
</property>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="database" value="ORACLE" />
<property name="generateDdl" value="false" />
</bean>
</property>
<property name="jpaProperties">
<props>
....
<prop key="hibernate.transaction.auto_close_session">true</prop>
<prop key="hibernate.connection.release_mode">auto</prop>
<prop key="hibernate.transaction.jta.platform">org.hibernate.service.jta.platform.internal.WeblogicJtaPlatform
</prop>
</props>
</property>
</bean>
<!-- ################################################################################################################ -->
<tx:jta-transaction-manager />
<jpa:repositories base-package="com.x.repo" />
When I try to call truncate method it gives "javax.persistence.TransactionRequiredException: Executing an update/delete query".I also try to use spring transactional anotation,it doesn't work.
It seems you should use
import org.springframework.transaction.annotation.Transactional;
instead of
import javax.transaction.Transactional;

spring - hibernate 5 naming strategy configuration

I am writing application using postgresql database and spring + hibernate frameworks.
I upgraded spring framework from 4.1.5.RELEASE to 4.2.0.RELEASE version and upgraded hibernate framework from 4.3.7.Final to 5.0.0.Final version.
After upgrade I have problems with NamingStrategy. In postgresql database, table column names are in lowercase separated by underscore, in application layer, bean properties are in camelcase.
This is working spring configuration file for older version:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<context:component-scan base-package="fms" />
<bean id="microFmsDataSource" class="org.apache.tomcat.jdbc.pool.DataSource" destroy-method="close">
<property name="driverClassName" value="org.postgresql.Driver" />
<property name="url" value="***" />
<property name="username" value="***" />
<property name="password" value="***" />
<property name="testOnBorrow" value="true" />
<property name="testOnReturn" value="true" />
<property name="testWhileIdle" value="true" />
<property name="validationQuery" value="select 1" />
<property name="initialSize" value="5" />
<property name="minIdle" value="10" />
<property name="maxIdle" value="100" />
<property name="maxActive" value="100" />
<property name="removeAbandoned" value="true" />
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="microFmsDataSource"/>
<property name="packagesToScan">
<list>
<value>fms</value>
</list>
</property>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
<property name="jpaPropertyMap">
<map>
<entry key="hibernate.cache.provider_class" value="org.hibernate.cache.NoCacheProvider" />
<entry key="hibernate.hbm2ddl.auto" value="validate" />
<entry key="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect" />
<entry key="hibernate.show_sql" value="true" />
<entry key="hibernate.format_sql" value="true" />
<entry key="hibernate.ejb.naming_strategy" value="org.hibernate.cfg.ImprovedNamingStrategy" />
</map>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
After upgrade I changed NamingStrategy configuration:
<entry key="hibernate.ejb.naming_strategy" value="org.hibernate.cfg.ImprovedNamingStrategy" />
like this:
<entry key="hibernate.implicit_naming_strategy" value="***" />
and tried all variants of options listed in hibernate javadoc: https://docs.jboss.org/hibernate/orm/5.0/javadocs/org/hibernate/cfg/AvailableSettings.html#IMPLICIT_NAMING_STRATEGY
but with no success.
Can you tell me what is alternative of ImprovedNamingStrategy in Hibernate 5 and provide a working configuration example ?
I think I found the solution.
To achieve my goal, I used hibernate.physical_naming_strategy configuration, instead of hibernate.implicit_naming_strategy.
I created an implementation of the PhysicalNamingStrategy interface which simulates part of the functionality of the original ImprovedNamingStrategy class:
package fms.util.hibernate;
import org.apache.commons.lang.StringUtils;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
public class ImprovedNamingStrategy implements PhysicalNamingStrategy {
#Override
public Identifier toPhysicalCatalogName(Identifier identifier, JdbcEnvironment jdbcEnv) {
return convert(identifier);
}
#Override
public Identifier toPhysicalColumnName(Identifier identifier, JdbcEnvironment jdbcEnv) {
return convert(identifier);
}
#Override
public Identifier toPhysicalSchemaName(Identifier identifier, JdbcEnvironment jdbcEnv) {
return convert(identifier);
}
#Override
public Identifier toPhysicalSequenceName(Identifier identifier, JdbcEnvironment jdbcEnv) {
return convert(identifier);
}
#Override
public Identifier toPhysicalTableName(Identifier identifier, JdbcEnvironment jdbcEnv) {
return convert(identifier);
}
private Identifier convert(Identifier identifier) {
if (identifier == null || StringUtils.isBlank(identifier.getText())) {
return identifier;
}
String regex = "([a-z])([A-Z])";
String replacement = "$1_$2";
String newName = identifier.getText().replaceAll(regex, replacement).toLowerCase();
return Identifier.toIdentifier(newName);
}
}
After I created this class, I changed my configuration from:
<entry key="hibernate.ejb.naming_strategy" value="org.hibernate.cfg.ImprovedNamingStrategy" />
to this:
<entry key="hibernate.physical_naming_strategy" value="fms.util.hibernate.ImprovedNamingStrategy" />
and now everything works correctly.
This solution covers only small part of ImprovedNamingStrategy. In my project, for table mapping and join mapping, I always specify the name of table or join column explicitly. I rely on implicit name conversion only for column names. So this simple solution was acceptable for me.
This is a full example of my Spring configuration file:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<context:component-scan base-package="fms" />
<bean id="microFmsDataSource" class="org.apache.tomcat.jdbc.pool.DataSource" destroy-method="close">
<property name="driverClassName" value="org.postgresql.Driver" />
<property name="url" value="***" />
<property name="username" value="***" />
<property name="password" value="***" />
<property name="testOnBorrow" value="true" />
<property name="testOnReturn" value="true" />
<property name="testWhileIdle" value="true" />
<property name="validationQuery" value="select 1" />
<property name="initialSize" value="5" />
<property name="minIdle" value="10" />
<property name="maxIdle" value="100" />
<property name="maxActive" value="100" />
<property name="removeAbandoned" value="true" />
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="microFmsDataSource"/>
<property name="packagesToScan">
<list>
<value>fms</value>
</list>
</property>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
<property name="jpaPropertyMap">
<map>
<entry key="hibernate.cache.provider_class" value="org.hibernate.cache.NoCacheProvider" />
<entry key="hibernate.hbm2ddl.auto" value="validate" />
<entry key="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect" />
<entry key="hibernate.show_sql" value="true" />
<entry key="hibernate.format_sql" value="true" />
<entry key="hibernate.physical_naming_strategy" value="fms.util.hibernate.ImprovedNamingStrategy" />
</map>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
I hope this solution will be helpful for somebody. :)
For anyone looking for a Java Config Solution
The hibernate.ejb.naming_strategy property seems split into two parts in hibernate 5.X:
hibernate.physical_naming_strategy
hibernate.implicit_naming_strategy
Spring provides SpringImplicitNamingStrategy and SpringPhysicalNamingStrategy for this purpose.
Here is my approach:
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy;
import org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
#Configuration
#DependsOn("myDataSource")
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "myEntityManagerFactory",
basePackages={"com.myapp.repo"},
transactionManagerRef="myTransactionManager"
)
public class MyJpaConfig {
private Map<String, Object> properties;
public MyJpaConfig() {
properties = new HashMap<>();
properties.put("hibernate.implicit_naming_strategy", SpringImplicitNamingStrategy.class.getName());
properties.put("hibernate.physical_naming_strategy", SpringPhysicalNamingStrategy.class.getName());
}
#Bean(name = "myEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder,
#Qualifier("systemDataSource") DataSource dataSource) {
LocalContainerEntityManagerFactoryBean build = builder.dataSource(dataSource)
.packages("com.myapp.entity")
.properties(properties)
.build();
return build;
}
#Bean(name = "myTransactionManager")
public PlatformTransactionManager myTransactionManager(
#Qualifier("myEntityManagerFactory") EntityManagerFactory myEntityManagerFactory) {
return new JpaTransactionManager(myEntityManagerFactory);
}
}
I had the exactly same problem. I fixed it with default implementation from spring boot:
<property name="hibernate.implicit_naming_strategy" value="org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy" />
<property name="hibernate.physical_naming_strategy" value="org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy" />
This work for me, from http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/orm/hibernate5/LocalSessionFactoryBean.html
<bean id="sessionFactory"
class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<!-- convert aaBb to aa_bb -->
<property name="PhysicalNamingStrategy">
<bean class="fms.util.hibernate.ImprovedNamingStrategy" />
</property>
<!-- convert aa_bb to aaBb -->
<property name="ImplicitNamingStrategy">
<bean class="org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyHbmImpl" />
</property>
...
</bean>
try this:
#Bean
public LocalSessionFactoryBean getSessionFactory() {
LocalSessionFactoryBean localSessionFactoryBean = new LocalSessionFactoryBean();
localSessionFactoryBean.setDataSource(getDataSource());
localSessionFactoryBean.setHibernateProperties(getHibernateProperties());
localSessionFactoryBean.setPackagesToScan(new String[]{"com.xxx.pojo"});
// -----important-----
localSessionFactoryBean.setPhysicalNamingStrategy(new CustomNamingStrategy());
return localSessionFactoryBean;
}
public class CustomNamingStrategy extends PhysicalNamingStrategyStandardImpl {***
Please find below 3 points that I discovered while working on Naming Strategy:
If you are providing #Table and #Column annotation in your entity classes with names provided with an underscore i.e. user_id i.e. #Column(name="user_id"), it will take the column name as user_id; if you give it as userid then it will change to user_id if you use no strategy or implicit strategy (specifically org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyHbmImpl). So, if you want a strategy where the entity attribute name changes to one with underscore and lowercase letters i.e. something from userId to user_id, you should use implicit or no strategy (which actually uses implicit strategy).
If you don't want your naming strategy to add an underscore to the column name or class name, then the strategy that you need to use would look like:
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl. The things that you provide in annotations #Table and #Column’s name attribute would remain as it is.
If you don't want to provide annotations and want to manually handle the table name and column names, you should extend the class org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl and override the required methods. If you still use annotations for some of the cases here, remember the overridden methods will apply on the names written in those annotations.

XStream JavaBeanConverter Spring config to serialize using getter

I would like to register XStream JavaBeanConverter in Spring config. I see test examples where JavaBeanConverter is registered as below.
xstream.registerConverter(new JavaBeanConverter(xstream.getClassMapper(), "class"), -20);
But how do I set it up in my spring config.
Currently my spring config is setup as below
<bean
class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="order" value="1" />
<property name="mediaTypes">
<map>
<entry key="json" value="application/json" />
<entry key="xml" value="application/xml" />
</map>
</property>
<property name="defaultViews">
<list>
<bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" >
<property name="renderedAttributes" value="document" />
</bean>
<bean class="org.springframework.web.servlet.view.xml.MarshallingView">
<property name="marshaller">
<bean class="org.springframework.oxm.xstream.XStreamMarshaller" p:autodetectAnnotations="true">
<property name="encoding" value="UTF-8" />
</bean>
</property>
<property name="contentType" value="application/xml;charset=UTF-8" />
<property name="modelKey" value="person" />
</bean>
</list>
</property>
<property name="ignoreAcceptHeader" value="true" />
</bean>
I am using xstream annotation for Alias and Converter.
I tried extending my custom converter from JavaBeanConverter. It serializes normal properties fine but I want it to serialize getXXX method.
public class MyCustomConverter extends JavaBeanConverter {
public MyCustomConverter(Mapper mapper) {
super(mapper);
}
#Override... marshal.. unmarshal... canConvert methods...
}
Thanks a lot!
Add this to your marshaller bean definition:
<property name="converters">
<util:list>
<bean class="com.MyBeanConverter">
<constructor-arg value="com.MyBean" index="0" />
</bean>
</util:list>
</property>
And then, define the following class that will implement the conversion
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.javabean.JavaBeanConverter;
public class MyBeanConverter extends JavaBeanConverter {
public MyBeanConverter(Class<?> theClass) {
super(new XStream().getMapper(), theClass);
}
}

how to extend a list in spring config

I have defined a with some "common" values. How can I extend the common list by additional values to various new beans?
<util:list id="myCommonList" list-class="java.util.LinkedList">
<bean .../>
<bean .../>
<bean .../>
<bean .../>
</util:list>
<bean id="extension" parent="myCommonList">
<bean ref="additionalValue"/>
</bean>
Will this overwrite the list or extend it?
You can do it, but not using <util:list>, which is just a convenience syntax. The container does provide a "collection merging" function, but you have to use the "old" style:
<bean id="parent" class="org.springframework.beans.factory.config.ListFactoryBean">
<property name="sourceList">
<list>
<value>X</value>
</list>
</property>
</bean>
<bean id="child" parent="parent" class="org.springframework.beans.factory.config.ListFactoryBean">
<property name="sourceList">
<list merge="true">
<value>Y</value>
</list>
</property>
</bean>
Based on skaffman's answer, you can achive this way:
<util:list id="parent">
<value>X</value>
</util:list>
<bean id="child" parent="parent" class="org.springframework.beans.factory.config.ListFactoryBean">
<property name="sourceList">
<list merge="true">
<value>Y</value>
</list>
</property>
</bean>
To append list2's elements to list1 use the following spring config.
<util:list id="list1">
<value>1</value>
<util:list>
<util:list id="list2">
<value>3</value>
<value>5</value>
<util:list>
<bean
class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject" ref="list1" />
<property name="targetMethod" value="addAll" />
<property name="arguments" ref="list2" />
</bean>
According to this JIRA; there is no trivial solution for this (currently, but hopefully in 3.1 there will be), though there are several workarounds; e.g. this one.
Today I was having this issue too. For me the acceptable solution was to use SpEL and while SpEL doesn't supports multiple statements - create two auxiliary classes that appends lists.
Context might be implemented like this:
<util:list id="list1">
<value>str1</value>
<value>str2</value>
</util:list>
<util:list id="list2">
<value>str3</value>
<value>str4</value>
</util:list>
<bean ...>
<property name="propertyThatRequiresMergedList"
value="#{ new x.y.springs.StringListsMerger().merge(#list1, #list2) }" />
</bean>
And classes:
package x.y.springs;
import java.util.List;
public abstract class ListsMerger<T extends List> {
public T merge(T ... lists) {
T container = createContainer();
for (T list : lists) {
container.addAll(list);
}
return container;
}
public abstract T createContainer();
}
package x.y.springs;
import java.util.ArrayList;
import java.util.List;
public class StringListsMerger extends ListsMerger<List<String>> {
#Override
public List<String> createContainer() {
return new ArrayList<String>();
}
}
You can overwrite or add any other bean in this list by following the below snippet.
For example: this is your bean which needs to be injected in the AAA util:list mentioned below.
<bean id = "BBB" class=""/>
<util:list id="AAA"/>
<bean id="AAAListMergeDirective" depends-on="AAA"
parent="listMergeDirective" >
<property name="add" ref="BBB" />
</bean>

Resources