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(分页)

这里的几篇文章都将注解形式搭建涉及的源码讲述得非常详细了,我后面就不重复详细步骤了

SSM全注解框架搭建+分页案例

SSM全注解框架搭建+分页案例

接下来说一下改造过程(可以直接删除所有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)

运行效果

SSM全注解框架搭建+分页案例

另附源代码:https://download.****.net/download/qq_38553950/10743774

github:https://github.com/houfanGitHub/ssm-page-noxml-demo