首先展示下项目结构(也就是在上一个ssm框架案例上面进行改造)
编写项目参考地址:
https://blog.csdn.net/qq_38200548/article/details/79889263?utm_source=blogxgwz2
https://blog.csdn.net/ahageete/article/details/79568739
https://blog.csdn.net/houysx/article/details/80197344
https://blog.csdn.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关联的xml文件(applicationContext.xml)
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对应的xml文件(spring-jdbc.xml/spring-service.xml ....)
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.csdn.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.csdn.net/download/qq_38553950/10743774
github:https://github.com/houfanGitHub/ssm-page-noxml-demo