引言
先前做的java项目中一直使用的EclipseLink和Hibernate,两种ORM框架,现在用Mybatis实现,当然它们之间各有各的优点,同样也有缺点,有时候,一个项目中是可以存在两种框架一起使用的,在项目中,技术选型很重要。Mybatis是一个持久层的框架,是apache下的顶级项目,mybatis让程序将主要的精力放在sql上,通过mybatis提供的映射方式,自由灵活生成。
Mybatis的主要是靠sql语句来进行实现对数据库持久化的,这就是mybatis的硬伤,当然,它也因为是靠sql语句进行实现,很灵活但不好维护。
画了一张简单的思维导图,咱们按照图进行说明:
mybatis的框架:
我们现在说一个Mybatis的框架,当然能用图说明,我们尽量不要使用文字:
对于图的说明:数据库链接的初期,只要使用时就会创建,不适用立刻释放,对数据库进行很频繁链接开启和关闭,造成数据库资源浪费,影响了数据库的性能。因此我们需要使用数据库连接池进行管理数据库的连接。这里我们需要使用sqlSessionFactory会话工厂进行管理。另外我们将sql语句硬编码到java代码中,如果sql语句修改,需要重新编译java代码,不利于系统维护,这里我们使用了将sql语句配置在xml配置文件中,即使sql变化,不需要对java代码进行编译了。另外,我们向proparedStatement中设置参数,对战位富豪位置和设置参数,也要放到xml文件中。从另外我们总是从resultSet中便利结果集数据,不利于系统维护,因此,我们将查询的结果集,自动映射成java对象。
简单的流程:
由于篇幅有限,我们直接在Spring整合工程中提到。
与spring的整合:
这里我们使用的是Mapper代理的形式,将我们所有的Mybatis的mapper代理放在SqlSessionFactory中进行管理,另外,将SqlSessionFactory嫁给Spring容器管理。具体:通过SqlSessionFactoryBuilder创建会话工厂SqlSessionFactory,将SqlSessionFactoryBuilder当成一个工具类使用即可,不需要使用单例管理SqlSessionFactoryBuilder。在需要创建SqlSessionFactory时候,只需要new一次SqlSessionFactoryBuilder即可。通过SqlSessionFactory创建sqlSession,通过单利模式管理SqlSessionFactory,交给Spring来进行管理。
我们整合的思路是,需要spring通过单例方式管理SqlSessionFactory,spring和mybatis整合生成代理对象,使用SQLSessionFactory创建SqlSession。(spring和mybatis整合自动完成)持久层的mapper需要由spring进行管理。
整合代码:
1、jar包
2、创建配置文件
首先看我们的目录结构:
mybatis下面的配置SqlMapConfig.xml
<span style="font-size:18px;"><?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> <!-- 批量别名定义 指定包名,mybatis自动扫描包中的po类,自动定义别名,别名就是类名(首字母大写或小写都可以) --> <package name="com.tgb.ncre.po" /> </typeAliases> <!-- 加载 映射文件 --> <mappers> <!--1、 可以一个一个的添加 --> <!--<mapper resource="sqlmap/User.xml" /> --> <!--2、建议使用, 批量加载mapper 指定mapper接口的包名,mybatis自动扫描包下边所有mapper接口进行加载 遵循一些规范:需要将mapper接口类名和mapper.xml映射文件名称保持一致,且在一个目录 中 上边规范的前提是:使用的是mapper代理方法 和spring整合后,使用mapper扫描器,这里不需要配置了 --> <package name="com.tgb.ncre.mapper"/> </mappers> </configuration></span>
db.properties文件:
<span style="font-size:18px;">jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/mybatis jdbc.username=root jdbc.password=root</span>
添加log4j.properties文件:
<span style="font-size:18px;"># Global logging configuration #\u5728\u5f00\u53d1\u73af\u5883\u4e0b\u65e5\u5fd7\u7ea7\u522b\u8981\u8bbe\u7f6e\u6210DEBUG\uff0c\u751f\u4ea7\u73af\u5883\u8bbe\u7f6e\u6210info\u6216error log4j.rootLogger=DEBUG, stdout # Console output... log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n</span>
sqlmap文件夹下我们暂时不用。是用dao直接注入SqlSessionFactory的时候,才用到的,我们这里用到的是在mapper中直接添加xml文件的。
下面是最重要的spring的applicationContext.xml文件,我们在这个文件下进行注入SqlSession的。
<span style="font-size:18px;"><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd "> <!-- 加载配置文件 --> <context:property-placeholder location="classpath:db.properties" /> <!-- 数据源,使用dbcp --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${jdbc.driver}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> <property name="maxActive" value="10" /> <property name="maxIdle" value="5" /> </bean> <!-- sqlSessinFactory --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 加载mybatis的配置文件 --> <property name="configLocation" value="mybatis/SqlMapConfig.xml" /> <!-- 数据源 --> <property name="dataSource" ref="dataSource" /> </bean> <!-- 原始dao接口 --> <bean id="userDao" class="com.tgb.ncre.dao.UserDaoImpl"> <property name="sqlSessionFactory" ref="sqlSessionFactory"/> </bean> <!-- mapper配置 MapperFactoryBean:根据mapper接口生成代理对象 --> <bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean"> <!--mapperInterface指定mapper接口 --> <property name="mapperInterface" value="com.tgb.ncre.mapper.UserMapper"/> <property name="sqlSessionFactory" ref="sqlSessionFactory"/> </bean> <!-- mapper批量扫描,从mapper包中扫描出mapper接口,自动创建代理对象并且在spring容器中注册 遵循规范:将mapper.java和mapper.xml映射文件名称保持一致,且在一个目录 中 自动扫描出来的mapper的bean的id为mapper类名(首字母小写) --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!-- 指定扫描的包名 如果扫描多个包,每个包中间使用半角逗号分隔 --> <property name="basePackage" value="com.tgb.ncre.mapper"/> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> </bean> </beans></span>
配置文件就建好了,是这样的:
我们现在创建src文件中,我们的pojo的java兑现个,和mapper代理。
我们先看src的文件目录:
User类:
<span style="font-size:18px;">package cn.itcast.ssm.po; import java.io.Serializable; import java.util.Date; import java.util.List; public class User implements Serializable { //属性名和数据库表的字段对应 private int id; private String username;// 用户姓名 private String sex;// 性别 private Date birthday;// 生日 private String address;// 地址 //用户创建的订单列表 private List<Orders> ordersList; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } @Override public String toString() { return "User [id=" + id + ", username=" + username + ", sex=" + sex + ", birthday=" + birthday + ", address=" + address + "]"; } public List<Orders> getOrdersList() { return ordersList; } public void setOrdersList(List<Orders> ordersList) { this.ordersList = ordersList; } } </span>
order类:
<span style="font-size:18px;">package com.tgb.ncre.po; import java.util.Date; import java.util.List; public class Orders { private Integer id; private Integer userId; private String number; private Date createtime; private String note; //用户信息 private User user; //订单明细 private List<Orderdetail> orderdetails; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public Integer getUserId() { return userId; } public void setUserId(Integer userId) { this.userId = userId; } public String getNumber() { return number; } public void setNumber(String number) { this.number = number == null ? null : number.trim(); } public Date getCreatetime() { return createtime; } public void setCreatetime(Date createtime) { this.createtime = createtime; } public String getNote() { return note; } public void setNote(String note) { this.note = note == null ? null : note.trim(); } public User getUser() { return user; } public void setUser(User user) { this.user = user; } public List<Orderdetail> getOrderdetails() { return orderdetails; } public void setOrderdetails(List<Orderdetail> orderdetails) { this.orderdetails = orderdetails; } }</span>
Orderdetail类:
<span style="font-size:18px;">package com.tgb.ncre.po; public class Orderdetail { private Integer id; private Integer ordersId; private Integer itemsId; private Integer itemsNum; //明细对应的商品信?? private Items items; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public Integer getOrdersId() { return ordersId; } public void setOrdersId(Integer ordersId) { this.ordersId = ordersId; } public Integer getItemsId() { return itemsId; } public void setItemsId(Integer itemsId) { this.itemsId = itemsId; } public Integer getItemsNum() { return itemsNum; } public void setItemsNum(Integer itemsNum) { this.itemsNum = itemsNum; } public Items getItems() { return items; } public void setItems(Items items) { this.items = items; } @Override public String toString() { return "Orderdetail [id=" + id + ", ordersId=" + ordersId + ", itemsId=" + itemsId + ", itemsNum=" + itemsNum + "]"; } }</span>
Items类:
<span style="font-size:18px;">package com.tgb.ncre.po; import java.util.Date; public class Items { private Integer id; private String name; private Float price; private String pic; private Date createtime; private String detail; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name == null ? null : name.trim(); } public Float getPrice() { return price; } public void setPrice(Float price) { this.price = price; } public String getPic() { return pic; } public void setPic(String pic) { this.pic = pic == null ? null : pic.trim(); } public Date getCreatetime() { return createtime; } public void setCreatetime(Date createtime) { this.createtime = createtime; } public String getDetail() { return detail; } public void setDetail(String detail) { this.detail = detail == null ? null : detail.trim(); } }</span>
然后,我们看com.tgb.ncre.mapper:
Usermapper.java
<span style="font-size:18px;">package com.tgb.ncre.mapper; import static org.junit.Assert.*; import com.tgb.ncre.po.User; public interface UserMapper { //根据id查询用户信息 public User findUserById(int id) throws Exception; } </span>
UserMapper.xml:
<span style="font-size:18px;"><?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"> <!-- namespace命名空间,作用就是对sql进行分类化管理,理解sql隔离 注意:使用mapper代理方法开发,namespace有特殊重要的作用,namespace等于mapper接口地址 --> <mapper namespace="com.tgb.ncre.mapper.UserMapper"> <select id="findUserById" parameterType="int" resultType="user"> SELECT * FROM USER WHERE id=#{value} </select> </mapper> </span>
注意:大家一定要这里的两个mapper包中的java类和xml的名称一定要相同才行,否则,在applicationContext.xml中创建SqlSession的时候,扫描不到所有的文件。
好了,现在我们就进行test:
右击我们的UserMapper.java,选择“New”——“Others”:
之后,我们写代码:
<span style="font-size:18px;">package com.tgb.ncre.mapper; import org.junit.Before; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.tgb.ncre.po.User; public class UserMapperTest { private ApplicationContext applicationContext; @Before public void setUp() throws Exception { applicationContext = new ClassPathXmlApplicationContext("classpath:spring/applicationContext.xml"); } @Test public void testFindUserById() throws Exception { UserMapper userMapper = (UserMapper) applicationContext.getBean("userMapper"); User user = userMapper.findUserById(1); System.out.println(user); } } </span>
Ok了,运行,JunitTest:
总结:
我们在研究持久层的框架的时候,要从多方面考虑,当我们学习Mybatis的时候,就要想有没有其他的ORM框架,拿出来进行比较。这样,我们才能很好地做技术选型,另外,之后,我们在学习Mybatis的时候,还要想到,如何才能一步一步优化。