SSM全注解框架搭建+分页案例
首先展示下项目结构(也就是在上一个ssm框架案例上面进行改造)
编写项目参考地址:
https://blog.****.net/qq_38200548/article/details/79889263?utm_source=blogxgwz2
https://blog.****.net/ahageete/article/details/79568739
https://blog.****.net/houysx/article/details/80197344
https://blog.****.net/sunroyfcb/article/details/80768204(分页)
这里的几篇文章都将注解形式搭建涉及的源码讲述得非常详细了,我后面就不重复详细步骤了
接下来说一下改造过程(可以直接删除所有xml,也可以跟着步骤慢慢添加删除)
一、因为无xml配置,就要解决因为无web.xml pom.xml报错的问题
在pom.xml中添加代码(web3.0之后可以基于注解开发 不需要web.xml)
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.1.0</version>
</plugin>
</plugins>
</build>
因为后面会用到分页插件 这里就直接在pom.xml文件中将jar包补齐
<pagehelper.version>5.1.2</pagehelper.version>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>${pagehelper.version}</version>
</dependency>
二、添加取代xml文件的Java代码
1.可以去理解为原有的web.xml
package com.ssm.config.web;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import com.ssm.config.listener.ContextLoaderListenerConfig;
import com.ssm.config.servlet.DispatchServletConfig;
public class WebConfiguration extends AbstractAnnotationConfigDispatcherServletInitializer {
/**
* 返回带有@Configuration注解的类会用来定义ContextLoaderListener
* 创建ApplicationContext中的bean(非web组件)
*/
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] {ContextLoaderListenerConfig.class};
}
/**
* 返回带有@Configuration注解的类用来定义DispatcherServlet上下文中的bean(web组件)
*/
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] {DispatchServletConfig.class};
}
/**
* 请求路径配置,配置为"/"表示它将处理所有请求
*/
@Override
protected String[] getServletMappings() {
return new String[] {"/"};
}
}
2.可以去理解为spring的dispatcherServlet
package com.ssm.config.servlet;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
@Configuration
@ComponentScan(basePackages="com.ssm.controller")
@EnableWebMvc
public class DispatchServletConfig extends WebMvcConfigurerAdapter {
//配置视图解析器
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/jsp/");
resolver.setSuffix(".jsp");
return resolver;
}
/**
* 没有此配置,dispatcherServlet会映射为应用的默认servlet,
* 所以他为处理所有请求,包括静态资源,如图片的样式表
* 这并不是我们想要的
*/
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
3.可以去理解为contextLoaderListener
package com.ssm.config.listener;
import java.io.IOException;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.ibatis.plugin.Interceptor;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import com.alibaba.druid.pool.DruidDataSource;
import com.github.pagehelper.PageInterceptor;
@Configuration
@ComponentScan(basePackages= "com.ssm.service")
@EnableTransactionManagement
@MapperScan(basePackages="com.ssm.mapper")
@PropertySource("classpath:config/database.properties")
public class ContextLoaderListenerConfig {
//数据源
@Bean
public DataSource getDataSource(@Value("${jdbc.driverClassName}")String driverClassName,
@Value("${jdbc.url}")String url,
@Value("${jdbc.username}")String username,
@Value("${jdbc.password}")String password) {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setUrl(url);
druidDataSource.setDriverClassName(driverClassName);
druidDataSource.setUsername(username);
druidDataSource.setPassword(password);
return druidDataSource;
}
/**配置PageInterceptor插件*/
@Bean
public PageInterceptor getPageInterceptor(){
PageInterceptor pageIntercptor=new PageInterceptor();
Properties properties=new Properties();
properties.setProperty("helperDialect", "mysql");
properties.setProperty("reasonable", "true");
properties.setProperty("pageSizeZero", "true");
properties.setProperty("supportMethodsArguments", "true");
properties.setProperty("returnPageInfo", "check");
properties.setProperty("params", "count=countSql");
pageIntercptor.setProperties(properties);
return pageIntercptor;
}
//sqlSessionFacrotyBean
@Bean
public SqlSessionFactoryBean getSqlSessionFactoryBean(DataSource dataSource,PageInterceptor pageInterceptor) throws IOException {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
sqlSessionFactoryBean.setPlugins(new Interceptor[] {pageInterceptor});
return sqlSessionFactoryBean;
}
//事务管理
@Bean
public DataSourceTransactionManager getdatasouDataSourceTransactionManager(DataSource dataSource) {
DataSourceTransactionManager manager = new DataSourceTransactionManager();
manager.setDataSource(dataSource);
return manager;
}
}
这里就可以将web.xml spring-*.xml 全部干掉了
后面就该mybatis的mapper.xml干掉了,改造原有的mapper接口
复杂的sql语句有两种方式 一种是直接在注解上写sql以<script>包含,
如有感兴趣的可以看这篇文章 https://blog.****.net/qq_32786873/article/details/78297551
SQL看上去比较杂乱,个人推荐用以下方式(当然也可以采用原本的XML形式)
package com.ssm.mapper;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.SelectProvider;
import org.apache.ibatis.annotations.UpdateProvider;
import org.springframework.stereotype.Repository;
import com.ssm.pojo.Users;
@Repository
public interface UsersMapper {
@Delete(value="delete from users where uid = #{uid}")
int deleteByPrimaryKey(Integer uid);
@Insert("insert into users(name,username,password,gender,age,createdate,lastlogindate,locked) "
+ "value(#{name},#{username},#{password},#{gender},#{age},#{createdate},#{lastlogindate},#{locked})")
int insert(Users record);
@Select("select * from users where uid = #{uid}")
Users selectByPrimaryKey(Integer uid);
@UpdateProvider(type=UsersProvider.class,method="updateByPrimaryKeySelective")
int updateByPrimaryKeySelective(Users record);
@SelectProvider(type=UsersProvider.class,method="findAll")
List<Users> findAll(Users users);
}
package com.ssm.mapper;
import org.apache.ibatis.jdbc.SQL;
import com.ssm.pojo.Users;
public class UsersProvider {
public String findAll(Users users) {
return new SQL() {
{
SELECT("*");
FROM("USERS");
if(users.getName() != null && !users.getName().trim().equals("")) {
WHERE("and name like concat('%','#{name}','%')");
}
if(users.getUsername() != null && !users.getUsername().trim().equals("")) {
WHERE("and username like concat('%','#{username}','%')");
}
}
}.toString();
}
public String updateByPrimaryKeySelective(Users users) {
return new SQL() {
{
UPDATE("USERS");
if(users.getName() != null && !users.getName().trim().equals("")) {
SET("name = #{name}");
}
if(users.getPassword() != null && !users.getPassword().trim().equals("")) {
SET("password = #{password}");
}
if(users.getGender() != null && !users.getGender().trim().equals("")) {
SET("gender = #{gender}");
}
if(users.getAge() != null) {
SET("age = #{age}");
}
WHERE("uid = #{uid}");
}
}.toString();
}
}
这里需要强调一个传参问题
原项目代码其实是这样的
@SelectProvider(type=UsersProvider.class,method="findAll")
List<Users> findAll(@Param("name")String name,@Param("username")String username);
public String findAll(String name,String username) {
return new SQL() {
{
SELECT("*");
FROM("USERS");
if(name != null && !name.trim().equals("")) {
WHERE("and name like concat('%','#{name}','%')");
}
if(username != null && !username.trim().equals("")) {
WHERE("and username like concat('%','#{username}','%')");
}
}
}.toString();
}
用过mybatis的mapper.xml开发的都知道是不需要配置parameterType的
<select id="findAll" resultMap="BaseResultMap">
select * from users
<where>
<if test="#{name} != null and #{name} != ''">
and name like concat('%',#{name},'%')
</if>
<if test="#{username} != null and #{username} != ''">
and name like concat('%',#{name},'%')
</if>
</where>
</select>
同理 注解形式也会报错。具体原因 上源代码 org.apache.ibatis.builder.annotation.ProviderSqlSource
private SqlSource createSqlSource(Object parameterObject) {
try {
int bindParameterCount = providerMethodParameterTypes.length - (providerContext == null ? 0 : 1);
String sql;
if (providerMethodParameterTypes.length == 0) {
sql = (String) providerMethod.invoke(providerType.newInstance());
} else if (bindParameterCount == 0) {
sql = (String) providerMethod.invoke(providerType.newInstance(), providerContext);
} else if (bindParameterCount == 1 &&
(parameterObject == null || providerMethodParameterTypes[(providerContextIndex == null || providerContextIndex == 1) ? 0 : 1].isAssignableFrom(parameterObject.getClass()))) {
sql = (String) providerMethod.invoke(providerType.newInstance(), extractProviderMethodArguments(parameterObject));
} else if (parameterObject instanceof Map) {
@SuppressWarnings("unchecked")
Map<String, Object> params = (Map<String, Object>) parameterObject;
sql = (String) providerMethod.invoke(providerType.newInstance(), extractProviderMethodArguments(params, providerMethodArgumentNames));
} else {
throw new BuilderException("Error invoking SqlProvider method ("
+ providerType.getName() + "." + providerMethod.getName()
+ "). Cannot invoke a method that holds "
+ (bindParameterCount == 1 ? "named argument(@Param)": "multiple arguments")
+ " using a specifying parameterObject. In this case, please specify a 'java.util.Map' object.");
}
Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
return sqlSourceParser.parse(replacePlaceholder(sql), parameterType, new HashMap<String, Object>());
} catch (BuilderException e) {
throw e;
} catch (Exception e) {
throw new BuilderException("Error invoking SqlProvider method ("
+ providerType.getName() + "." + providerMethod.getName()
+ "). Cause: " + e, e);
}
}
这里需要满足4种方式的其中一种,还需要在我们自定义的方法中获取参数值进行判断来拼接条件,所以后面就将两个参数合为一个Users对象了(用Map也可以)
map和users的区别也就在参数的接收形式上有些差异
//map形式接收
public String findAll(String name,String username);
//users对象形式接收
public String findAll(Users users)
运行效果
另附源代码:https://download.****.net/download/qq_38553950/10743774