框架(Framework)是整个或者部分系统的可重用设计,是J2EE底层技术的封装。框架是可以被应用开发者(程序员)定制的应用骨架。简而言之,框架其实就是别人搭好了舞台,你来做表演。
在J2EE体系中,有着各种各样的技术。不同的软件企业,根据自身的业务需求选择不同的技术,容易造成应用依赖技术,增加了项目开发实现的复杂性和技术风险性。企业项目中应该将应用的设计与实现技术解耦。
企业项目中使用框架,程序员不再需要重复造轮子,**只需要专注实现业务需求。**使用框架的方便性,提升了开发效率。
一个成熟的框架,经过了在众多企业项目中的验证使用。稳定性有保障。
在J2EE应用中,通常把项目整体进行分层设计。有表现层、业务层、持久层。
关于持久层的框架,还有一个封装程度更高的框架(hibernate)。该框架相对比较重量级,以及其它各个方面的原因,目前流行程度下降了很多,企业项目中用的越来越少了。
mybatis是Apache软件基金会下的一个开源项目,前身是Ibatis框架。2010年这个项目由apache软件基金会迁移到googlecode下,改名为mybatis。2013年11月又迁移到了github。
mybatis是一个持久层的框架,是对jdbc操作数据库的封装,使开发者只需要关注业务本身,不需要花费精力去处理加载驱动、创建数据库连接对象、创建statement语句对象、参数设置、结果集处理等一系列繁杂的过程代码。
mybatis框架通过xml或注解进行配置,将java对象与sql语句中的参数自动映射生成最终执行的sql语句,并将sql语句执行结果自动映射成java对象。
三句话概括介绍:
mybatis框架早期版本叫做Ibatis,目前源代码托管在github
mybatis框架是对jdbc操作数据库的封装,是一个持久层的框架
mybatis框架通过xml或者注解进行配置,实现java对象与sql语句的自动对应关系(映射)
查询全部用户数据。
mybatis框架快速入门事项 | 需求:查询全部用户列表。 |
---|---|
1 | 创建项目 |
2 | 配置pom.xml,加入mybatis框架依赖包 |
3 | 准备配置文件:sqlMapConfig.xml,log4j.properties |
4 | 编写用户实体类(User) |
5 | 编写用户dao接口(UserDao) |
6 | 编写用户dao接口映射文件(UserDao.xml) |
7 | 在sqlMapConfig.xml中,加载UserDao.xml |
8 | 编写测试代码 |
mybatis框架包
mysql数据库驱动包
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>mybatisgroupId>
<artifactId>mybatis_quickstartartifactId>
<version>1.0-SNAPSHOTversion>
<dependencies>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.4.6version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.47version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
dependency>
dependencies>
project>
说明:它是mybatis框架的核心配置文件,是执行入口。
<configuration>
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/zzw?characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
dataSource>
environment>
environments>
<mappers>
<mapper resource="dao/maps/UserDaoMap.xml"/>
mappers>
configuration>
package com.po;
public class User {
private int id;
private String username;
private String birthday;
private String sex;
private String address;
public User() {
}
public User(int id, String username, String birthday, String sex, String address) {
this.id = id;
this.username = username;
this.birthday = birthday;
this.sex = sex;
this.address = address;
}
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 getBirthday() {
return birthday;
}
public void setBirthday(String birthday) {
this.birthday = birthday;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", birthday='" + birthday + '\'' +
", sex='" + sex + '\'' +
", address='" + address + '\'' +
'}';
}
}
package com.dao;
import com.po.User;
import java.util.List;
public interface UserDao {
List<User> findAll();
}
<mapper namespace="com.dao.UserDao">
<select id="findAll" resultType="com.po.User">
select * FROM user
select>
mapper>
InputStream in = App.class.getResourceAsStream(“SqlMapConfig.xml”);建议是使用本类。不要乱用其他类的class文件。
import com.dao.UserDao;
import com.po.User;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import java.io.InputStream;
import java.util.List;
public class App {
@Test
public void test() throws Exception {
//获取文件流
InputStream in = App.class.getResourceAsStream("SqlMapConfig.xml");
//创建工厂的构建器
SqlSessionFactoryBuilder factoryBuilder = new SqlSessionFactoryBuilder();
//创建SqlSession的工厂
SqlSessionFactory factory = factoryBuilder.build(in);
//创建SqlSession
SqlSession sqlSession = factory.openSession();
//创建接口的代理对象
UserDao userDao = sqlSession.getMapper(UserDao.class);
//调用方法
List<User> users = userDao.findAll();
for (User user : users) {
System.out.println(user);
}
sqlSession.close();
in.close();
}
}
序号 | 技术点 | 说明 |
---|---|---|
1 | xml配置文件解析 | dom4j |
2 | jdbc连接数据库 | mysql |
3 | 数据库连接池 | c3p0 |
4 | 单元测试框架 | junit |
5 | 工厂设计模式 | 工厂模式是我们最常用的实例化对象的模式,是用工厂方法代替new操作的一种模式。工厂模式在Java程序系统可以说是随处可见。因为工厂模式就相当于创建实例对象的new,我们经常要根据类Class生成实例对象,如A a=new A() 工厂模式也是用来创建实例对象的。 |
6 | 反射技术 | JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。 |
7 | 代理 | jdk提供的动态代理Proxy。 |
8 | 泛型 | 在编写代码时不确定类型,在使用时指定类型 |
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>TestgroupId>
<artifactId>DemoartifactId>
<version>1.0-SNAPSHOTversion>
<properties>
<dom4j.vesrion>1.6.1dom4j.vesrion>
<jaxen.version>1.1.6jaxen.version>
<mysql.version>5.1.30mysql.version>
<c3p0.version>0.9.2.1c3p0.version>
<junit.version>4.12junit.version>
properties>
<dependencies>
<dependency>
<groupId>dom4jgroupId>
<artifactId>dom4jartifactId>
<version>${dom4j.vesrion}version>
dependency>
<dependency>
<groupId>jaxengroupId>
<artifactId>jaxenartifactId>
<version>${jaxen.version}version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>${mysql.version}version>
dependency>
<dependency>
<groupId>com.mchangegroupId>
<artifactId>c3p0artifactId>
<version>${c3p0.version}version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>${junit.version}version>
dependency>
dependencies>
project>
将入门程序中的User、UserDao、sqlMapConfig.xml、UserDao.xml拷贝到项目中
说明:Mapper类用于封装UserDao.xml文件内容。
package config;
/**
* 封装接口配置文件内容
*/
public class Mapper {
private String namespace;// 名称空间
private String id;// select标签id属性
private String resultType;// select标签resultType属性
private String queryString;// sql语句
public String getNamespace() {
return namespace;
}
public void setNamespace(String namespace) {
this.namespace = namespace;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getResultType() {
return resultType;
}
public void setResultType(String resultType) {
this.resultType = resultType;
}
public String getQueryString() {
return queryString;
}
public void setQueryString(String queryString) {
this.queryString = queryString;
}
}
说明:Configuration类用于封装sqlMapConfig.xml配置内容。
package config;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.beans.PropertyVetoException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 封装主配置文件sqlMapConfig.xml内容
*/
public class Configuration {
// 连接数据库的四个基本要素
private String driver;
private String url;
private String username;
private String password;
// 数据源对象
private ComboPooledDataSource dataSource;
// 接口映射文件的配置(Mapper)
private Map<String, Mapper> mappers;
/**
* 思考:
* 1.解析主配置文件内容
* 2.创建数据源对象
*/
public Configuration() {
// 1.解析主配置文件
loadSqlMapConfig();
// 2.创建数据源对象
createDataSource();
}
/**
* 解析主配置文件内容
*/
private void loadSqlMapConfig() {
try {
// 1.加载主配置文件
InputStream inputStream = this.getClass().getClassLoader()
.getResourceAsStream("sqlMapConfig.xml");
// 2.通过dom4j解析配置文件
SAXReader reader = new SAXReader();
Document document = reader.read(inputStream);
Element rootElement = document.getRootElement();
// 3.查找到property标签
//
List<Element> list = rootElement.selectNodes("//property");
for (Element e : list) {
// 获取属性值
String name = e.attributeValue("name");
String value = e.attributeValue("value");
if ("driver".equals(name)) {
driver = value;
}
if ("url".equals(name)) {
url = value;
}
if ("username".equals(name)) {
username = value;
}
if ("password".equals(name)) {
password = value;
}
}
// 4.查找mapper标签
//
List<Element> mapperList = rootElement.selectNodes("//mapper");
for (Element e : mapperList) {
String resource = e.attributeValue("resource");
// 解析接口映射文件(UserDao.xml)
loadMapper(resource);
}
} catch (DocumentException e) {
e.printStackTrace();
}
}
/**
* 创建数据源对象
*/
private void createDataSource() {
dataSource = new ComboPooledDataSource();
// 设置连接数据库的四个基本要素
try {
dataSource.setDriverClass(driver);
dataSource.setJdbcUrl(url);
dataSource.setUser(username);
dataSource.setPassword(password);
} catch (PropertyVetoException e) {
e.printStackTrace();
}
}
/**
* 解析接口配置文件
*/
private void loadMapper(String filePath) {
try {
// 1加载接口映射文件
InputStream inputStream = this.getClass().getClassLoader()
.getResourceAsStream(filePath);
// 2.通过dom4j解析接口配置文件
SAXReader reader = new SAXReader();
Document document = reader.read(inputStream);
Element rootElement = document.getRootElement();
// 3.获取较名称空间
String namespace = rootElement.attributeValue("namespace");
// 4.获取根元素的所有子元素
List<Element> elements = rootElement.elements();
for (Element e : elements) {
/**
*
*/
String id = e.attributeValue("id");
String resultType = e.attributeValue("resultType");
String queryString = e.getText();
// 封装Mapper对象
putMappers(namespace, id, resultType, queryString);
}
} catch (DocumentException e) {
e.printStackTrace();
}
}
/**
* 封装Mapper对象
*/
private void putMappers(String namespace, String id, String resultType, String queryString) {
// 如果mappers为空,就创建
if (mappers == null) {
mappers = new HashMap<String, Mapper>();
}
// 创建Mapper对象
Mapper mapper = new Mapper();
mapper.setId(id);
mapper.setQueryString(queryString);
mapper.setResultType(resultType);
mapper.setNamespace(namespace);
// hashmap 的Key
String key = namespace + "." + id;
mappers.put(key, mapper);
}
/**
* =======================getter/setter==================
*/
public String getDriver() {
return driver;
}
public void setDriver(String driver) {
this.driver = driver;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public ComboPooledDataSource getDataSource() {
return dataSource;
}
public void setDataSource(ComboPooledDataSource dataSource) {
this.dataSource = dataSource;
}
public Map<String, Mapper> getMappers() {
return mappers;
}
public void setMappers(Map<String, Mapper> mappers) {
this.mappers = mappers;
}
}
package test;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import config.Configuration;
import config.Mapper;
import core.SqlSession;
import core.SqlSessionFactory;
import dao.UserDao;
import org.junit.Test;
import po.User;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 自定义框架测试类
*/
public class FrameWorkTest {
/**
* 1.测试解析配置文件
*/
@Test
public void configurationTest() {
Configuration configuration = new Configuration();
// 测试接口配置文件解析
Map<String, Mapper> mappers = configuration.getMappers();
Set<String> keys = mappers.keySet();
Iterator<String> iter = keys.iterator();
while (iter.hasNext()) {
Mapper mapper = mappers.get(iter.next());
System.out.println("mapper:" + mapper);
}
// 测试数据源对象
ComboPooledDataSource dataSource = configuration.getDataSource();
System.out.println("数据源:" + dataSource);
}
}
说明:SqlSession类中,提供获取持久层接口代理对象方法。
package core;
import java.lang.reflect.Proxy;
/**
* 核心组件之SqlSession,提供一个方法,这个方法返回dao接口的代理对象
*/
public class SqlSession {
/**
* 这里用到的技术:
* 1.泛型技术
* 2.动态代理技术
* 涉及到的类:Proxy
* 涉及到的方法:newProxyInstance()
* 参数:
* Classlaoder:类加载器,不需要关心
* interfaces:接口列表
* InvocationHandler:如何增强功能
*/
public <T> T getMapper(Class<T> type){
// 创建接口的代理对象
T proxy =(T) Proxy.newProxyInstance(
this.getClass().getClassLoader(),
new Class[]{type},
new MapperProxyFactory()
);
return proxy;
}
}
package core;
/**
* 工厂类,创建SqlSession对象
*/
public class SqlSessionFactory {
/**
*创建SqlSession
*/
public static SqlSession openSqlSession(){
return new SqlSession();
}
}
说明:在编写SqlSession类的getMapper方法中,需要返回持久层接口的代理对象,目前还是返回null。将在MapperProxyFactory类中,通过动态代理方式创建代理对象。
package core;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import config.Configuration;
import config.Mapper;
import util.Executor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
/**
* 实现增强功能的类,它需要实现接口InvocationHandler
*/
public class MapperProxyFactory implements InvocationHandler{
/**
*反射调用的方法:invoke
* 参数列表:
* proxy:代理对象的引用,不需要关心
* method:当前执行的方法对象
* args:当前执行的方法的参数
*
* 思考:该方法中需要做什么事情?
* 1.解析配置文件内容,获取到相关性
* 2.根据配置文件内容,得到执行的sql语句
* 3.根据配置文件内容,得到数据源对象
* 4.执行数据库操作,返回数据库执行结果
*
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 1.获取到当前执行的方法名称
// findAllUsers
String methodName = method.getName();
// 2.获取到接口的全路径
// dao.UserDao
String className = method.getDeclaringClass().getName();
// 3.解析配置文件内容
Configuration configuration = new Configuration();
// 4.根据配置文件内容,得到执行的sql语句
Map<String, Mapper> mappers = configuration.getMappers();
Mapper mapper = mappers.get(className + "." + methodName);
// 5.根据配置文件内容,得到数据源对象
ComboPooledDataSource dataSource = configuration.getDataSource();
System.out.println("执行的sql语句:"+mapper.getQueryString());
System.out.println("数据源对象:"+dataSource);
List<Object> userList = Executor.selectList(mapper, dataSource.getConnection());
return userList;
}
}
package test;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import config.Configuration;
import config.Mapper;
import core.SqlSession;
import core.SqlSessionFactory;
import dao.UserDao;
import org.junit.Test;
import po.User;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 自定义框架测试类
*/
public class FrameWorkTest {
/**
* 1.测试解析配置文件
*/
@Test
public void configurationTest() {
Configuration configuration = new Configuration();
// 测试接口配置文件解析
Map<String, Mapper> mappers = configuration.getMappers();
Set<String> keys = mappers.keySet();
Iterator<String> iter = keys.iterator();
while (iter.hasNext()) {
Mapper mapper = mappers.get(iter.next());
System.out.println("mapper:" + mapper);
}
// 测试数据源对象
ComboPooledDataSource dataSource = configuration.getDataSource();
System.out.println("数据源:" + dataSource);
}
/**
* 测试框架的核心组件
*/
@Test
public void coreFrameworkTest(){
// 1.创建sqlSession
SqlSession sqlSession = SqlSessionFactory.openSqlSession();
// 2.获取接口的代理对象
UserDao userDao = sqlSession.getMapper(UserDao.class);
// 3.调用方法执行
List<User> list = userDao.findAllUsers();
for(User u:list){
System.out.println(u);
}
}
}
到此我们已经准备好了要执行的sql语句,数据库连接对象。只需要把静态的用户数据,替换成数据库中查询的用户数据即可。
package util;
import config.Mapper;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.List;
/**
* 执行数据库操作工具类
*/
public class Executor {
/**
* 执行数据库操作,返回执行结果
* @param mapper:sql语句映射对象
* @param connection:数据库连接对象
*/
public static <T> List<T> selectList(Mapper mapper, Connection connection){
// 1.定义statement语句对象和执行结果集ResultSet对象
PreparedStatement psmt = null;
ResultSet rs = null;
// 2.返回结果集
List<T> list = new ArrayList<T>();
try{
// 3.获取查询的sql语句
String sql = mapper.getQueryString();
// 4.获取返回值类型
String resultType = mapper.getResultType();
// 转换成字节码
Class<?> clazz = Class.forName(resultType);
// 5.创建statement语句对象
psmt = connection.prepareStatement(sql);
// 6.执行查询
rs = psmt.executeQuery();
// 7.处理结果集
handleResult(list,clazz,rs);
}catch(Exception e){
e.printStackTrace();
}finally {
// 8. 释放资源
close(connection,psmt,rs);
}
return list;
}
/**
* 处理结果集
* @param list
* @param clazz
* @param rs
*/
private static void handleResult(List list,Class<?> clazz,ResultSet rs){
try {
// 1.获取元数据。元数据中包含有查询列的数量和列名称
ResultSetMetaData metaData = rs.getMetaData();
int columnCount = metaData.getColumnCount();// 列的数量
// 列的名称数组(细节:列的名称,约定与对象中的属性名称一致)
String[] columns = new String[columnCount];
for(int i=1;i<=columnCount;i++){
columns[i-1] = metaData.getColumnName(i);
}
//System.out.println(Arrays.asList(columns));
// 2.循环处理结果集
while(rs.next()){
// 2.1.实例化对象
Object obj = clazz.newInstance();
// 2.2.取出字段值,赋值给obj对象属性
for(int i=0;i<columns.length;i++){
Object cv = rs.getObject(columns[i]);
// 通过反射技术给对象属性赋值
PropertyDescriptor pd = new PropertyDescriptor(columns[i],clazz);
// 获取set方法
Method writeMethod = pd.getWriteMethod();
// 赋值
writeMethod.invoke(obj,cv);
}
list.add(obj);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 释放资源
* @param con
* @param psmt
* @param rs
*/
public static void close(Connection con, PreparedStatement psmt, ResultSet rs){
try{
if(rs != null) rs.close();
if(psmt != null) psmt.close();
if(con != null) con.close();
}catch(Exception e){
e.printStackTrace();
}
}
}