SSM整合(2): spring 与 mybatis 整合
在进行完spring与springmvc整合之后, 继续 spring与mybatis的整合.
既然是操作数据库, 那必然不能缺少了连接属性
一. db.properties
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8 jdbc.username=root jdbc.password=root
二. application.xml
将前一篇注释的部分, 再注释回来就行了, 是一个import操作
三. spring和mybatis的整合xml文件: spring-mybatis.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 自动搜索bean --> <!-- <context:annotation-config/>--> <!-- 自动扫描 --> <!--<context:component-scan base-package="org.elvin.ssm" />--> <!-- 引入配置文件 --> <context:property-placeholder location="classpath:conf/db.properties" /> <!--配置数据库连接池--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> <!--driverClassName会自动识别, 可以不配置--> <property name="driverClassName" value="${jdbc.driver}" /> <!-- 配置初始化大小、最小、最大 --> <!-- initialSize:初始化时建立物理连接的个数, 默认为0 minIdle:最小连接池数量 maxActive:最大连接池数量, 默认为8 --> <property name="initialSize" value="1" /> <property name="minIdle" value="1" /> <property name="maxActive" value="20" /> <!--获取连接时最大等待时间,单位ms--> <property name="maxWait" value="60000" /> <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 --> <property name="timeBetweenEvictionRunsMillis" value="60000" /> <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 --> <property name="minEvictableIdleTimeMillis" value="300000" /> <!-- validationQuery:用来检测连接是否有效的sql testWhileIdle:建议配置为true,默认false, 不影响性能,并且保证安全性,申请连接的时候检测,如果空闲时间大于 timeBetweenEvictionRunsMillis, 执行validationQuery检测连接是否有效。 testOnBorrow:申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。默认true testOnReturn:归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能 --> <property name="validationQuery" value="SELECT 1 " /> <property name="testWhileIdle" value="true" /> <property name="testOnBorrow" value="false" /> <property name="testOnReturn" value="false" /> <!-- 打开PSCache,并且指定每个连接上PSCache的大小 --> <!--<property name="poolPreparedStatements" value="true" /> <property name="maxPoolPreparedStatementPerConnectionSize" value="20" />--> <!-- 监控统计用的filter:stat 日志用的filter:log4j 防御sql注入的filter:wall --> <!-- 配置监控统计拦截的filters,去掉后监控界面sql无法统计 --> <property name="filters" value="stat" /> </bean> <!-- mybatis 的工厂 --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="configLocation" value="classpath:conf/mybatis.xml"/> <!-- 自动扫描mapping.xml文件 --> <property name="mapperLocations" value="classpath:mapper/**/*.xml" /> </bean> <!-- DAO接口所在包名,Spring会自动查找其下的类 --> <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="org.elvin.ssm.mapper" /> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> </bean> <!-- 配置事务管理器 --> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <!-- 注解方式配置事物 --> <!-- <tx:annotation-driven transaction-manager="transactionManager" /> --> <!-- 拦截器方式配置事物 --> <tx:advice id="txAdvice" transaction-manager="txManager"> <tx:attributes> <tx:method name="get*" isolation="REPEATABLE_READ" read-only="true" /> <tx:method name="find*" isolation="REPEATABLE_READ" read-only="true" /> <tx:method name="search*" isolation="REPEATABLE_READ" read-only="true" /> <tx:method name="load*" isolation="REPEATABLE_READ" read-only="true" /> <tx:method name="*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" /> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="pointCut" expression="execution(* org.elvin.ssm.service..*.*(..))" /> <aop:advisor advice-ref="txAdvice" pointcut-ref="pointCut" /> </aop:config> </beans>
四. mybatis本身也可以有一个对自己的配置文件. mybatis.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <typeAliases> <package name="org.elvin.ssm.pojo" /> </typeAliases> </configuration>
如果没有什么特别的配置, 这里也可以不写, 这个文件也可以不要, 但是要在spring-mybatis.xml文件中删除 configLocation 的配置
五. 加入日志文件 log4j.properties
log4j.rootLogger=DEBUG,Console,FILE
#Console
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n
#File Appender
log4j.appender.FILE=org.apache.log4j.DailyRollingFileAppender
log4j.appender.FILE.File=C:/soft/logs/SSM.log
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.ConversionPattern=[%d{HH:mm:ss,SSS}] [%l] [%t] [%-5p] : %m%n
log4j.logger.java.sql.ResultSet=INFO
log4j.logger.org.apache=INFO
log4j.logger.java.sql.Connection=INFO
log4j.logger.java.sql.Statement=INFO
log4j.logger.java.sql.PreparedStatement=INFO
然后需要在web.xml文件中加入配置
<!-- 加载log4j的配置文件log4j.properties --> <context-param> <param-name>log4jConfigLocation</param-name> <param-value> classpath:conf/log4j.properties </param-value> </context-param> <!-- 设定刷新日志配置文件的时间间隔,这里设置为10s --> <context-param> <param-name>log4jRefreshInterval</param-name> <param-value>10000</param-value> </context-param> <listener> <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class> </listener>
六. 后台代码
在mapper文件夹下, 加入一个mapper文件. BookMapper.java
package org.elvin.ssm.mapper; import org.elvin.ssm.pojo.Book; import java.util.List; /** * author: Elvin * datetime: 2017/12/2 8:21 * description: */ public interface BookMapper { /** * 根据id查询实体数据 * @param id * @return */ public Book find(Integer id); /** * 获取数据库中所有的数据 * @return */ public List<Book> getAll(); /** * 新增 * @param book * @return */ public Integer insert(Book book); /** * 修改 * @param book */ public void update(Book book); /** * 删除 * @param id */ public void remove(Integer id); }
这个文件是一个接口文件, 这里不需要我们手动去实现这个接口, 只需要在resources/mapper文件夹下, 加入对应的xml文件就可以了
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="org.elvin.ssm.mapper.BookMapper"> <select id="find" parameterType="java.lang.Integer" resultType="org.elvin.ssm.pojo.Book"> select * from book where id=#{id} </select> <select id="getAll" resultType="org.elvin.ssm.pojo.Book"> select * from book </select> <insert id="insert" parameterType="org.elvin.ssm.pojo.Book" > insert into book(name, price, publishTime) values(#{name}, #{price}, #{publishTime}) </insert> <update id="update" parameterType="org.elvin.ssm.pojo.Book"> update book set name=#{name}, price=#{price}, publishTime=#{publishTime} where id=#{id]} </update> <delete id="remove" parameterType="java.lang.Integer"> delete from book where id=#{id} </delete> </mapper>
这样, dao中的部分, 就完成了. 接下来, 完成service中的部分
package org.elvin.ssm.service; import org.elvin.ssm.pojo.Book; import java.util.List; public interface BookService { /** * 根据id查询实体数据 * @param id * @return */ public Book find(Integer id); /** * 获取数据库中所有的数据 * @return */ public List<Book> getAll(); /** * 新增 * @param book * @return */ public Integer insert(Book book); /** * 修改 * @param book */ public void update(Book book); /** * 删除 * @param id */ public void remove(Integer id); }
其实现类:
package org.elvin.ssm.serviceimpl; import com.alibaba.druid.support.logging.Log; import com.alibaba.druid.support.logging.LogFactory; import com.alibaba.fastjson.JSON; import org.elvin.ssm.mapper.BookMapper; import org.elvin.ssm.pojo.Book; import org.elvin.ssm.service.BookService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; /** * author: Elvin * datetime: 2017/12/2 8:26 * description: */ @Service("bookService") public class BookServiceImpl implements BookService { Log logger = LogFactory.getLog(BookServiceImpl.class); @Autowired private BookMapper bookMapper; /** * 根据id查询实体数据 * @param id * @return */ public Book find(Integer id){ logger.info("find book : " + id); return bookMapper.find(id); } /** * 获取数据库中所有的数据 * @return */ public List<Book> getAll(){ logger.info("getAll book"); return bookMapper.getAll(); } /** * 新增 * @param book * @return */ public Integer insert(Book book){ logger.info("insert book : " + JSON.toJSONString(book)); return bookMapper.insert(book); } /** * 修改 * @param book */ public void update(Book book){ logger.info("update book : " + JSON.toJSONString(book)); bookMapper.update(book); } /** * 删除 * @param id */ public void remove(Integer id){ logger.info("remove book : " + id); bookMapper.remove(id); } }
controller中, 新建一个控制器, 来验证一下程序是否能正常运行
package org.elvin.ssm.controller; import org.elvin.ssm.pojo.Book; import org.elvin.ssm.pojo.ResModel; import org.elvin.ssm.service.BookService; import org.joda.time.DateTime; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.*; import java.util.*; /** * author: Elvin * datetime: 2017/11/29 20:06 * description: */ @Controller @RequestMapping("hello") public class HelloController { @Autowired private BookService bookService; @RequestMapping("index") public String index(ModelMap model){ List<Book> bookList = bookService.getAll(); model.put("bookList", bookList); return "index"; } @RequestMapping(value = "book/{id}", method = {RequestMethod.GET, RequestMethod.POST}) @ResponseBody public Book book(@PathVariable("id") Integer id){ Book b = bookService.find(id); return b; } @RequestMapping(value = "get", method = RequestMethod.POST) public @ResponseBody Map<String, Object> getData(@RequestParam("id") Integer id){ Map<String, Object> map = new HashMap<>(); map.put("name", "hahhha"); map.put("age", 20); return map; } @GetMapping("addData") @ResponseBody public ResModel addData(){ ResModel resObj = new ResModel("新增数据失败", 0, null); try{ List<Book> list = getBookList(); list.forEach(n->{ bookService.insert(n); }); resObj.setMsg("新增成功"); resObj.setCode(1); } catch (Exception e){ System.out.println(e.getMessage()); } return resObj; } //region private method private List<Book> getBookList(){ List<Book> bookList = new ArrayList<>(); String[] nameStrs = {"吴", "一", "雪", "动", "额", "阿", "前", "里", "排"}; Random r = new Random(); String timeStr = new DateTime().toString("yyyy-MM-dd HH:mm:ss"); for (int i = 0; i < 10 ; i++){ Book b = new Book(); b.setId(i+1); b.setName(nameStrs[r.nextInt(5)] + nameStrs[r.nextInt(9)]); b.setPublishTime(timeStr); b.setPrice(r.nextInt(100)); bookList.add(b); } return bookList; } //endregion }
这里我做了一个Json数据返回的规范实体
package org.elvin.ssm.pojo; /** * author: Elvin * datetime: 2017/12/2 13:19 * description: */ public class ResModel { private String msg; private Integer code; private Object resObj; public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } public Object getResObj() { return resObj; } public void setResObj(Object resObj) { this.resObj = resObj; } public ResModel(String msg, Integer code, Object resObj) { this.msg = msg; this.code = code; this.resObj = resObj; } }
七. 验证
1. 加数据到数据库中
这里的价格和时间, 我在数据库中用的是 int 和 varchar 类型的. 在实际使用中, 价格不要使用decimal去存储, 看需求, 如果money不是很大, 可以用int, 如果大, 则使用long来存储. 这样能保证数据的精确性. 而对于时间类型, 使用字符串类型去处理, 其实完全是可以的, 并且在实际使用中, 我觉得更方便.
有了数据之后, 可以来查询一下