现在主流的持久化方案包括了IBatis以及Hibernate,两种方案的选型,这里不做阐述,因为笔者没用过Hibernate不敢随便评论。我选择IBatis是觉得自己写SQL做映射,很灵活,自己可以掌握SQL的设计。
参考:http://mybatis.org/mybatis-3/
MyBatis 是支持普通SQL查询,存储过程和高级映射的优秀持久层框架。MyBatis 消除了几乎所有的JDBC代码和参数的手工设置以及结果集的检索。MyBatis 使用简单的 XML或注解用于配置和原始映射,将接口和 Java 的POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。
每个MyBatis应用程序主要都是使用SqlSessionFactory实例的,一个SqlSessionFactory实例可以通过SqlSessionFactoryBuilder获得。SqlSessionFactoryBuilder可以从一个xml配置文件或者一个预定义的配置类的实例获得。
用xml文件构建SqlSessionFactory实例是非常简单的事情。推荐在这个配置中使用类路径资源(classpath resource),但你可以使用任何Reader实例,包括用文件路径或file://开头的url创建的实例。MyBatis有一个实用类—-Resources,它有很多方法,可以方便地从类路径及其它位置加载资源。
MyBatis入门指南
环境说明
基本的环境:需要MyBatis库以及JDBC的支持。
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.8</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.32</version>
</dependency>
使用说明
平时使用过JDBC程序的同学,都有这样的映像,首先创建一个MySQL连接,然后执行对应语句,解析语句将对应的返回结果转成POJO对象。Ibatis设计的目的就是完成语句和对象的映射,使用的时候配置对应数据源和映射关系,使用相关接口来完成对应数据哭操作。下面看一下具体使用方法:
Ibatis配置说明
数据源
数据源包括环境和映文件,环境就是明确数据库的用户名、密码、要访的数据库地以及数据库访问的驱动;映射文件是SQL语句和对象的映射关系
cat ibatis.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>
<properties>
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/test" />
<property name="username" value="root" />
<property name="password" value="123456" />
</properties>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="ibatis/example/User.xml"/>
</mappers>
</configuration>
#### mapper映射管理
mapper里面需要配置的就是映关系也就是mapper,
注意mapper的resource地址。资源文件一般都放在resources目录下,对应的地址默认从resources下加载,因此使用相对地址即可。
下面看一下mapper文件 ibatis/example/User.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.mybatis.example.UserMapper">
<select id="selectOneUser" resultType="User">
select * from user where id = #{id}
</select>
</mapper>
### Ibatis使用流程
从配置文件中加载对应的数据源和Mapper
String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();
try {
User user = (User) session.selectOne("org.mybatis.example.UserMapper.selectOneUser", 101);
} finally {
session.close();
}
MyBatis相关概念
对象的生命周期
提示对象生命周期和依赖注入框架依赖注入框架可以创建线程安全的、基于事务的SqlSession,和映射器(mapper)并将它们直接注入到你的bean中,因此可以直接忽略它们的生命周期。如果对如何通过依赖注入框架来使用 MyBatis 感兴趣可以研究一下 MyBatis-Spring 或 MyBatis-Guice 两个子项目。
SqlSessionFactoryBuilder
这个类可以被实例化、使用和丢弃,一旦创建了SqlSessionFactory,就不再需要它了。因此 SqlSessionFactoryBuilder实例的最佳范围是方法范围(也就是局部方法变量)。你可以用 SqlSessionFactoryBuilder来创建多个SqlSessionFactory 实例,但是最好还是不要让其一直存在以保证所有的 XML 解析资源开放给更重要的事情。-
SqlSessionFactory
SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由对它进行清除或重建。使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏味道(bad smell)”。因此 SqlSessionFactory 的最佳范围是应用范围。有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。
-
SqlSession
每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的范围是请求或方法范围。绝对不能将 SqlSession实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。也绝不能将 SqlSession 实例的引用放在任何类型的管理范围中,比如 Serlvet 架构中的 HttpSession。如果你现在正在使用一种 Web 框架,要考虑 SqlSession 放在一个和 HTTP 请求对象相似的范围中。换句话说,每次收到的 HTTP 请求,就可以打开一个 SqlSession,返回一个响应,就关闭它。这个关闭操作是很重要的,你应该把这个关闭操作放到 finally 块中以确保每次都能执行关闭。下面的示例就是一个确保 SqlSession 关闭的标准模式:SqlSession session = sqlSessionFactory.openSession(); try { // do work } finally { session.close(); }
在你的所有的代码中一致性地使用这种模式来保证所有数据库资源都能被正确地关闭。
-
映射器实例(Mapper Instances)
映射器是创建用来绑定映射语句的接口。映射器接口的实例是从 SqlSession 中获得的。因此从技术层面讲,映射器实例的最大范围是和 SqlSession 相同的,因为它们都是从 SqlSession 里被请求的。尽管如此,映射器实例的最佳范围是方法范围。也就是说,映射器实例应该在调用它们的方法中被请求,用过之后即可废弃。并不需要显式地关闭映射器实例,尽管在整个请求范围(request scope)保持映射器实例也不会有什么问题,但是很快你会发现,像 SqlSession 一样,在这个范围上管理太多的资源的话会难于控制。所以要保持简单,最好把映射器放在方法范围(method scope)内。下面的示例就展示了这个实践:
SqlSession session = sqlSessionFactory.openSession(); try { BlogMapper mapper = session.getMapper(BlogMapper.class); // do work } finally { session.close(); }
结合Spring
使用Spring需要在配置文件中添加applicationContext.xml文件,Spring容器启动时会
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis?characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="password"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--dataSource属性指定要用到的连接池-->
<property name="dataSource" ref="dataSource"/>
<!--configLocation属性指定mybatis的核心配置文件-->
<property name="configLocation" value="config/Configuration.xml"/>
</bean>
使用的时候:
private static ApplicationContext ctx;
static {
ctx = new ClassPathXmlApplicationContext("config/applicationContext.xml");
}
或者采用注解的方式来实现,建议使用注解,WebX是基于Spring的,可以直接支持Spring的这些特性,自己包装一下SqlSessionFactory。
@Service("sqlSessionFactory")
Class SqlSessionFactoryWraper {
SqlSession openSession() {
}
}
//使用
@Service("name=sqlSessionFactory")
private SqlSessionFactoryWraper sqlSessionFactory;
示例 DAO类
/**
* Copyright 2006-2015 handu.com
* <p/>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import java.io.Serializable;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
/**
* 通用DAO实现类
* @author Jinkai.Ma
*/
@Repository
public class Dao extends SqlSessionDaoSupport {
/**
* 设置工厂类
*
* @param sqlSessionFactory sql工厂类
*/
@Autowired
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
super.setSqlSessionFactory(sqlSessionFactory);
}
/**
* 插入
*
* @param prefix 前缀
* @param key 关键字
* @param object 参数对象
*/
public void insert(String prefix, String key, Object object) {
getSqlSession().insert(prefix + key, object);
}
/**
* 修改
*
* @param prefix 前缀
* @param key 关键字
* @param object 参数对象
*/
public void update(String prefix, String key, Object object) {
getSqlSession().update(prefix + key, object);
}
/**
* 删除
*
* @param prefix 前缀
* @param key 关键字
* @param id ID
*/
public void delete(String prefix, String key, Serializable id) {
getSqlSession().delete(prefix + key, id);
}
/**
* 删除
*
* @param key 关键字
* @param id ID
*/
public void delete(String key, Serializable id) {
getSqlSession().delete(key, id);
}
/**
* 删除
*
* @param prefix 前缀
* @param key 关键字
* @param object 对象
*/
public void delete(String prefix, String key, Object object) {
getSqlSession().delete(prefix + key, object);
}
/**
* 获取单条
*
* @param <T> 泛型
* @param prefix 前缀
* @param key 关键字
* @param params 参数
* @return T 泛型结果
*/
public <T> T get(String prefix, String key, Object params) {
return getSqlSession().selectOne(prefix + key, params);
}
/**
* 获取集合
*
* @param <T> 泛型
* @param prefix 前缀
* @param key 关键字
* @return T 泛型结果
*/
public <T> List<T> getList(String prefix, String key) {
return getSqlSession().selectList(prefix + key);
}
/**
* 按查询条件获取集合
*
* @param <T> 泛型
* @param prefix 前缀
* @param key 关键字
* @param params 参数
* @return T 泛型结果
*/
public <T> List<T> getList(String prefix, String key, Object params) {
return getSqlSession().selectList(prefix + key, params);
}
/**
* 获取数量
*
* @param prefix 前缀
* @param key 关键字
* @param params 参数
* @return Integer
*/
public Integer count(String prefix, String key, Object params) {
return getSqlSession().selectOne(prefix + key, params);
}
/**
* 分页
*
* @param prefix 前缀
* @param pageKey 分页关键字
* @param countKey 求数量关键字
* @param params 参数
* @param offset 偏移量
* @param limit 限定数量
* @return Object[]
*/
public Object[] page(String prefix, String pageKey, String countKey, Object params, int offset, int limit) {
return new Object[]{
getSqlSession().selectList(prefix + pageKey, params, new RowBounds(offset, limit)),
getSqlSession().selectOne(prefix + countKey, params)
};
}
/**
* 执行SQL语句
*
* @param sql SQL语句
* @return boolean
*/
public boolean executeSql(String sql) {
try {
return getSqlSession().getConnection().prepareStatement(sql).execute();
} catch (SQLException e) {
e.printStackTrace();
}
return false;
}
/**
* 执行查询SQL语句
*
* @param sql SQL语句
* @return List<Map>
*/
public List<Map> querySql(String sql) {
List<Map> list = Lists.newArrayList();
try {
ResultSet rs = getSqlSession().getConnection().prepareStatement(sql,
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_UPDATABLE).executeQuery();
try {
ResultSetMetaData rsm = rs.getMetaData(); //获得列集
int col = rsm.getColumnCount(); //获得列的个数
String[] colName = new String[col];
//取结果集中的表头名称, 放在colName数组中
for (int i = 0; i < col; i++) {
colName[i] = rsm.getColumnName(i + 1);
}
rs.beforeFirst();
while (rs.next()) {
Map<String, String> map = Maps.newHashMap();
for (String aColName : colName) {
map.put(aColName, rs.getString(aColName));
}
list.add(map);
}
} catch (SQLException e) {
e.printStackTrace();
return null;
}
} catch (SQLException e) {
e.printStackTrace();
}
return list;
}
}