iBatis2.X入门附带完整项目_JAVA_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > JAVA > iBatis2.X入门附带完整项目

iBatis2.X入门附带完整项目

 2013/8/15 6:43:51  DR-YangLong  程序员俱乐部  我要评论(0)
  • 摘要:虽然iBatis更名为MyBatis,同时版本也更新到3.X,并且功能也更加完善和方便使用,但对于很多人来说2.X的稳定性和安全性更值得留恋,更多的是遗留的项目大多都使用的2.X版本!刚开始学习ORM框架时,使用的是Hibernate,深感其功能的强大,但同时感觉到其效率优化是个很大的问题(看到其发出的HQL和SQL语句就觉得蛋碎还有各种参数的调整优化),没有很多的经验积累无法做出很好的性能优化!出现的情况就是反而不如使用JDBC开发时性能来得高。后来同学实习时他公司使用了ibatis
  • 标签:项目
    虽然iBatis更名为MyBatis,同时版本也更新到3.X,并且功能也更加完善和方便使用,但对于很多人来说2.X的稳定性和安全性更值得留恋,更多的是遗留的项目大多都使用的2.X版本!刚开始学习ORM框架时,使用的是Hibernate,深感其功能的强大,但同时感觉到其效率优化是个很大的问题(看到其发出的HQL和SQL语句就觉得蛋碎还有各种参数的调整优化),没有很多的经验积累无法做出很好的性能优化!出现的情况就是反而不如使用JDBC开发时性能来得高。后来同学实习时他公司使用了ibatis,看到SQLMAP时眼前一亮,心说这个就是我想要的,直接用SQL语言来做自己想做的事,在源头解决效率问题(优化SQL)总是令人愉快的,并且参数优化也相对简单。更主要的是自己练习的项目基本是中小型居多(太小的还是直接JDBC吧,用ibatis配置文件会把你搞烦的,虽然有ibator帮忙),此篇文章为学习总结整理,希望对那些想要入门的同学或同仁有所帮助!
    首先,你所要准备的东西有:ibatis、log4j架包,安装有ORACLE数据库或MySQL数据库(因为目前我只使用这2个 )以及相应的jdbc驱动包(ORACLE的驱动在安装目录里的product\11.2.0\dbhome_1\jdbc\lib这个下面,MySQL的google一下有很多)。
    接下来是代码部分,我全沾了,后面跟个打包,想要跑一下的(其实你直接下个去跑跑,改改也就入门了),MyEclipse直接导入,Eclipse建个同名项目,包复制进去,建立Java EE6 library和jdk1.6 library,导入驱动包和上面的包就行。
   
    目录结构
    Model(POJO)以及sqlmap,sqlmapcofig和DAO:
    Model:
class="java" name="code">
package model;

public class Ibatis {
private int id;
private String account;
private String paswd;
private String gender;
private String phone;

public String toString(){
	return "id:"+this.getId()+" account:"+this.getAccount()+" paswd:"+this.getPaswd()+" gender:"+this.getGender()+" phone:"+this.getPhone();
}
public int getId() {
	return id;
}
public void setId(int id) {
	this.id = id;
}
public String getAccount() {
	return account;
}
public void setAccount(String account) {
	this.account = account;
}
public String getPaswd() {
	return paswd;
}
public void setPaswd(String paswd) {
	this.paswd = paswd;
}
public String getGender() {
	return gender;
}
public void setGender(String gender) {
	this.gender = gender;
}
public String getPhone() {
	return phone;
}
public void setPhone(String phone) {
	this.phone = phone;
}

}


sqlmap(iBatis.xml):
<?xml version="1.0" encoding="UTF-8" ?>  
  
<!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"   
   "http://ibatis.apache.org/dtd/sql-map-2.dtd">

<sqlMap namespace="learn">
    
    <!-- 重命名类名,将其作为一个类型,可在返回和参数类型设置时使用,在此命名空间内有效 -->
	<typeAlias alias="iba" type="model.Ibatis" />
	
	<!-- 定义返回MAP,此MAP用于ibatis接受数据库返回数据时自动封装到相应POJO类中 -->
	<resultMap id="iBatis_Result" class="iba">
		<result property="id" column="id" />
		<result property="account" column="account" />
		<result property="paswd" column="paswd" />
		<result property="gender" column="gender" />
		<result property="phone" column="phone" />
		<!-- property属性与POJO类属性对应,column与数据库select语句中查询列名一致,
		如果用了别名,这里也要用别名。在解决一对多,多对多问题时,这里可以用<result property="POJO中子POJO对应LIst属性名" resultMap="命名空间.resultMap的ID" />的形式
		 前提是你已经为List的POJO设置了一个resultMap-->
	</resultMap>
	
	<!-- 数据查询 -->
	<select id="select_All" resultMap="iBatis_Result" parameterClass="string">
		SELECT * FROM IBATISLEARN
	</select>
	<!-- 用MAP作为参数类型,ibatis的取实参较为安全的形式为:
	#参数名#,使用like时为:“'%$参数名$%'” parameterClass默认为Map方式,即便你不定义,ibatis也自动将其作为Map方式处理,需传入对象需定义为对象-->
	<select id="select_Login" parameterClass="java.util.Map"
		resultMap="iBatis_Result">
		select
		id,
		account,
		paswd,
		gender,
		phone
		from IBATISLEARN
		where account = #account# and paswd = #paswd#<!-- 此处的参数名必须与map中的key值一样 -->
	</select>
	<select id="select_one" parameterClass="iba"
		resultMap="iBatis_Result">
		select
		id,
		account,
		paswd,
		gender,
		phone
		from IBATISLEARN
		where account = #account# and paswd = #paswd#<!-- 此处的参数名必须与对象中的属性名一样 -->
	</select>
	<!-- 动态条件查询 -->
	<select id="select_Dynamic" parameterClass="iba" resultMap="iBatis_Result">
	    select id,account,paswd,gender,phone
	    from IBATISLEARN
	 <!-- 当传入对象有某个属性不为空时(这里可以使用的判断条件还可以是其他的,不止isNotEmpty)。在上面SQL中加入where并追加条件 -->
     <dynamic prepend="where">  
         <isNotEmpty prepend="and" property="account"> 
                                account like '%$account$%'
         </isNotEmpty>
          <isNotEmpty prepend="and" property="paswd"> 
                                paswd = #paswd#
         </isNotEmpty> 
         <isNotEmpty prepend="and" property="gender"> 
                                gender = #gender# 
         </isNotEmpty> 
         <isNotEmpty prepend="and" property="phone"> 
                                phone like '%$phone$%' 
         </isNotEmpty>  
      </dynamic> 
	</select>
	<!-- 
	在配置文件中SQL语句可以分开来写:
	<sql id="sub_Select">
	select * from ibatislearn
	</sql>
	<sql id="sub_Where">
	where account like #account#
	</sql>
	<select id="all_select" parameterClass="iba" resultMap="iBatis_Result">
     <include refid="sub_Select"/> 
     <include refid="sub_Where"/> 
    </select>
	另外还可以将SQL语句写在XML的DATA节点中
	 -->
	
	<!-- 数据插入,数据插入时,ID如果是序列,可直接使用sequence.nextval插入ID,如果是数据库有自增,则不必插入ID -->
	<insert id="insert_Ibatis" parameterClass="iba">
	<selectKey resultClass="int" keyProperty="id" >
         SELECT IBATIS_ID.NEXTVAL AS id FROM dual
     </selectKey>
		insert into IBATISLEARN (
		id,
		account,
		paswd)
		values (#id#,
		#account#, #paswd#
		)
	</insert>
	<!-- 动态插入 -->
    <insert id="insert_Dynamic" parameterClass="iba" >
    <selectKey resultClass="int" keyProperty="id" >
         SELECT IBATIS_ID.NEXTVAL AS id FROM dual
     </selectKey>
    insert into IBATISLEARN
    <dynamic prepend="(" >
        id,
      <isNotNull prepend="," property="account" >
        account
      </isNotNull>
      <isNotNull prepend="," property="paswd" >
        paswd
      </isNotNull>
      <isNotNull prepend="," property="gender" >
        gender
      </isNotNull>
      <isNotNull prepend="," property="phone" >
        phone
      </isNotNull>
      )
    </dynamic>
    values
    <dynamic prepend="(" >
        #id#,
      <isNotNull prepend="," property="account" >
        #account#
      </isNotNull>
      <isNotNull prepend="," property="paswd" >
        #paswd#
      </isNotNull>
      <isNotNull prepend="," property="gender" >
        #gender#
      </isNotNull>
      <isNotNull prepend="," property="phone" >
        #phone#
      </isNotNull>
      )
   </dynamic>
   </insert>

    <!-- 更新 -->
	<update id="update_Ibatis" parameterClass="iba">
		update IBATISLEARN set
		paswd =
		#paswd#
		where
		account = #account#
	</update>
    <!-- 动态更新 -->
    <update id="update_Dynamic" parameterClass="iba" >
     update IBATISLEARN
      <dynamic prepend="set" >
        <isNotNull prepend="," property="account" >
          account = #account#
        </isNotNull>
        <isNotNull prepend="," property="paswd" >
          paswd = #paswd#
        </isNotNull>
        <isNotNull prepend="," property="gender" >
          gender = #gender#
        </isNotNull>
        <isNotNull prepend="," property="phone" >
          phone = #phone#
        </isNotNull>
      </dynamic>
     where id = #id#
    </update>
  
    <!-- 删除 -->
	<delete id="delete_Ibatis" parameterClass="string">
		delete from IBATISLEARN
		where account = #account#
	</delete>

</sqlMap>

SqlMapConfig.xml:
<?xml version="1.0" encoding="UTF-8"?>  
<!DOCTYPE sqlMapConfig PUBLIC "-//iBATIS.com//DTD SQL Map Config 2.0//EN"   
    "http://www.ibatis.com/dtd/sql-map-config-2.dtd">   

<sqlMapConfig>
<settings cacheModelsEnabled="true" enhancementEnabled="true"
		lazyLoadingEnabled="true" maxRequests="200" maxSessions="100"
		maxTransactions="20" useStatementNamespaces="true" />
<!-- 事务管理配置 -->   
<transactionManager type="JDBC">   
    <dataSource type="SIMPLE">   
      <property value="oracle.jdbc.driver.OracleDriver" name="JDBC.Driver"/>   
      <property value="jdbc:oracle:thin:@127.0.0.1:1521:orcl" name="JDBC.ConnectionURL"/>   
      <property value="scott" name="JDBC.Username"/>   
      <property value="tiger" name="JDBC.Password"/>   
    </dataSource>   
  </transactionManager>  
<!-- 映射文件配置,用"com/yanglong/dao/study.xml"的形式 --> 
<sqlMap resource="dao/iBatis.xml" />   
</sqlMapConfig>   

DAO(IbatisDAO.java):
package dao;

import java.io.IOException;
import java.io.Reader;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

import model.Ibatis;

import com.ibatis.common.resources.Resources;
import com.ibatis.sqlmap.client.SqlMapClient;
import com.ibatis.sqlmap.client.SqlMapClientBuilder;

/**
 * learn DAO
 * 
 * @author Dream.YangLong
 * 
 */
public class IbatisDAO {
	private Reader reader;
	private SqlMapClient sqlMap;
	
	public IbatisDAO(){
		try {
			reader = Resources.getResourceAsReader("SqlMapConfig.xml");
		} catch (IOException e) {
			System.out.println("reader初始化错误!");
			e.printStackTrace();
		}
		sqlMap = SqlMapClientBuilder.buildSqlMapClient(reader);
	}

	/**
	 * 查询单个对象数据
	 * 
	 * @param ib
	 *            封装有帐号和密码的对象
	 * @return 符合条件的单个对象
	 */
	public Ibatis queryOne(Ibatis ib) throws SQLException, IOException {
		ib = (Ibatis) sqlMap.queryForObject("learn.select_one", ib);
		return ib;
	}

	public Ibatis queryOne(Map<String, String> map) throws SQLException,
			IOException {
		Ibatis ib = (Ibatis) sqlMap.queryForObject("learn.select_Login", map);
		return ib;
	}

	/**
	 * 查询多条数据,1个亦可,但需传入主键或者唯一列值
	 * 
	 * @param ib
	 *            封装有查询条件的对象
	 * @return 含有信息的链表
	 */
	public List<Ibatis> queryMore(Ibatis ib) throws SQLException, IOException {
		@SuppressWarnings("unchecked")
		List<Ibatis> ibs = sqlMap.queryForList("learn.select_Dynamic", ib);
		return ibs;
	}

	/**
	 * 查询所有的记录
	 * 
	 * @return 所有记录
	 * @throws SQLException
	 * @throws IOException
	 */
	public List<Ibatis> queryMore() throws SQLException, IOException {
		@SuppressWarnings("unchecked")
		List<Ibatis> ibs = sqlMap.queryForList("learn.select_All", "1");
		return ibs;
	}

	/**
	 * 新增帐号密码(持久操作)
	 * 
	 * @param ib
	 *            需新增对象
	 * @throws SQLException
	 *             异常
	 */
	public void insertIbatis(Ibatis ib) throws SQLException {
		sqlMap.insert("learn.insert_Ibatis", ib);
	}

	/**
	 * 新增(持久操作)
	 * 
	 * @param ib
	 *            需新增对象
	 * @throws SQLException
	 *             异常
	 */
	public void insertIbatisDy(Ibatis ib) throws SQLException {
		sqlMap.insert("learn.insert_Dynamic", ib);
	}

	/**
	 * 更新数据
	 * 
	 * @param ib
	 *            更新对象,需要封装id入对象才能更新
	 * @throws SQLException
	 *             异常
	 */
	public void updateIbatis(Ibatis ib) throws SQLException {
		sqlMap.update("learn.update_Dynamic", ib);
	}

	/**
	 * 删除数据
	 * 
	 * @param account
	 *            需要删除的帐号
	 * @throws SQLException
	 *             异常
	 */
	public void deleteIbatis(String account) throws SQLException {
		sqlMap.delete("learn.delete_Ibatis", account);
	}

	public Reader getReader() {
		return reader;
	}

	public void setReader(Reader reader) {
		this.reader = reader;
	}

	public SqlMapClient getSqlMap() {
		return sqlMap;
	}

	public void setSqlMap(SqlMapClient sqlMap) {
		this.sqlMap = sqlMap;
	}
	
}



services(IbatisServices.java):
package services;

import java.io.IOException;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

import dao.IbatisDAO;
import model.Ibatis;

/**
 * ibatis学习services类,services类处理异常并产生日志
 * 
 * @author Dream.YangLong
 * 
 */
public class IbatisServices {
	private Ibatis iba;
	private IbatisDAO ibo=new IbatisDAO();
	private List<Ibatis> ibs;

	/**
	 * 新增数据,只有帐号密码
	 * 
	 * @param ib
	 *            需新增(持久化)对象
	 * @return true or false,成功或失败
	 */
	public boolean insertIbatis(Ibatis ib) {
		try {
			ibo.insertIbatis(ib);
		} catch (SQLException e) {
			System.out.println("新增失败!");
			e.printStackTrace();
			return false;
		}
		return true;
	}

	/**
	 * 动态新增数据,有什么增加什么
	 * 
	 * @param ib
	 *            需新增对象
	 * @return true or false
	 */
	public boolean insertIbatisDy(Ibatis ib) {
		try {
			ibo.insertIbatisDy(ib);
		} catch (SQLException e) {
			System.out.println("新增失败!");
			e.printStackTrace();
			return false;
		}
		return true;
	}

	/**
	 * 单个查询,测试使用pojo时查询
	 * 
	 * @param ib
	 *            封装有account和paswd字段的Ibatis对象
	 * @return 查询后所得结果封装成的对象
	 */
	public Ibatis queryOne(Ibatis ib) {
		try {
			ib = ibo.queryOne(ib);
		} catch (SQLException e) {
			System.out.println("queryOne(Ibatis ib)查询出现SQL异常!");
			e.printStackTrace();
			return null;
		} catch (IOException e) {
			System.out.println("queryOne(Ibatis ib)查询出现IO异常!");
			e.printStackTrace();
			return null;
		}
		return ib;
	}

	/**
	 * 单个查询,测试使用Map参数
	 * 
	 * @param map
	 *            有account和paswd属性的map
	 * @return 查询后所得结果封装成的对象
	 */
	public Ibatis login(Map<String, String> map) {
		try {
			iba = ibo.queryOne(map);
		} catch (SQLException e) {
			System.out.println("login(Map<String,String> map)查询出现SQL异常!");
			e.printStackTrace();
			return null;
		} catch (IOException e) {
			System.out.println("login(Map<String,String> map)查询出现IO异常!");
			e.printStackTrace();
			return null;
		}
		return iba;
	}

	/**
	 * 动态条件查询多个对象数据
	 * 
	 * @param ib
	 *            封装有条件的Ibatis对象
	 * @return 满足条件的ibatis对象链表
	 */
	public List<Ibatis> queryMore(Ibatis ib) {
		try {
			ibs = ibo.queryMore(ib);
		} catch (SQLException e) {
			System.out.println("queryMore(Ibatis ib)查询出现SQL异常!");
			e.printStackTrace();
			return null;
		} catch (IOException e) {
			System.out.println("queryMore(Ibatis ib)查询出现IO异常!");
			e.printStackTrace();
			return null;
		}
		return ibs;
	}

	/**
	 * 查询所有对象数据
	 * 
	 * @return 所有对象数据链表
	 */
	public List<Ibatis> queryMore() {
		try {
			ibs = ibo.queryMore();
		} catch (SQLException e) {
			System.out.println("queryMore()查询出现SQL异常!");
			e.printStackTrace();
			return null;
		} catch (IOException e) {
			System.out.println("queryMore()查询出现IO异常!");
			e.printStackTrace();
			return null;
		}
		return ibs;
	}

	/**
	 * 数据更新,此方法依赖对象id
	 * 
	 * @param ib
	 *            需要更新的对象,必须封装入ID
	 * @return true or false,成功或失败
	 */
	public boolean updateIbatis(Ibatis ib) {
		try {
			ibo.updateIbatis(ib);
		} catch (SQLException e) {
			System.out.println("更新失败!");
			e.printStackTrace();
			return false;
		}
		return true;
	}

	/**
	 * 删除数据,此方法依赖帐号
	 * 
	 * @param account
	 *            帐号
	 * @return true or false,成功或失败
	 */
	public boolean deleteIbatis(String account) {
		try {
			ibo.deleteIbatis(account);
		} catch (SQLException e) {
			System.out.println("删除失败!");
			e.printStackTrace();
			return false;
		}
		return true;
	}
}

测试类TestClass.java:
package test;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import services.IbatisServices;
import model.Ibatis;

/**
 * ibatis学习Services测试类
 * 
 * @author Dream.YangLong
 * 
 */
public class TestClass {
	private Ibatis ib;
	private IbatisServices ibss = new IbatisServices();
	private List<Ibatis> ibs;
	private static Logger logger = Logger.getLogger(TestClass.class);

	// 新增,只有帐号密码
	public void addIba() {
		ib = new Ibatis();
		ib.setAccount("testaccount1");
		ib.setPaswd("testpaswd1");
		boolean flag = ibss.insertIbatis(ib);
		if (flag)
			System.out.println("新增成功!");
		else
			System.out.println("新增失败!");
	}

	/**
	 * 新增,动态信息
	 */
	public void addIbaDy() {
		ib = new Ibatis();
		ib.setAccount("testaccount2");
		ib.setPaswd("testpaswd2");
		ib.setGender("女");
		boolean flag = ibss.insertIbatisDy(ib);
		if (flag)
			System.out.println("新增成功!");
		else
			System.out.println("新增失败!");
	}

	/**
	 * 单个查询,测试POJO
	 */
	public void queryOne() {
		ib = new Ibatis();
		ib.setAccount("testaccount1");
		ib.setPaswd("testpaswd1");
		ib = ibss.queryOne(ib);
		if (ib != null)
			System.out.println("单个查询,测试pojo:" + ib.toString());
		else
			System.out.println("查询为空!");
	}

	/**
	 * map查询
	 */
	public void login() {
		Map<String, String> map = new HashMap<String, String>();
		map.put("account", "testaccount1");
		map.put("paswd", "testpaswd1");
		ib = ibss.login(map);
		if (ib != null)
			System.out.println("单个查询,测试map:" + ib.toString());
		else
			System.out.println("查询为空!");
	}

	/**
	 * 多个查询,模糊匹配
	 */
	public void queryMore() {
		ib = new Ibatis();
		System.out.println(ib.getId());
		ib.setPhone("1");
		ib.setAccount("test");
		ibs = ibss.queryMore(ib);
		
		for (Ibatis ibe : ibs) {
			System.out.println(ibe.toString());
			System.out.println("=========================");
		}

	}

	/**
	 * 多个查询,查询所有
	 */
	public void queryAll() {
		ibs = ibss.queryMore();
		for (Ibatis ibe : ibs) {
			System.out.println(ibe.toString());
			System.out.println("=========================");
		}
	}

	/**
	 * 根据ID动态更新
	 */
	public void updateInfo() {
		ib = new Ibatis();
		ib.setId(1);
		ib.setAccount("updatetest1");
		boolean flag = ibss.updateIbatis(ib);
		if (flag)
			System.out.println("更新成功!");
		else
			System.out.println("更新失败!");

	}

	/**
	 * 根据帐号删除信息
	 */
	public void deleteInfo() {
		boolean flag = ibss.deleteIbatis("testaccount2");
		if (flag)
			System.out.println("删除成功!");
		else
			System.out.println("删除失败!");
	}

	public Ibatis getIb() {
		return ib;
	}

	public void setIb(Ibatis ib) {
		this.ib = ib;
	}

	public IbatisServices getIbss() {
		return ibss;
	}

	public void setIbss(IbatisServices ibss) {
		this.ibss = ibss;
	}

	public static void main(String[] args) {
		TestClass test = new TestClass();
		logger.info("测试");
		// 新增
		test.addIba();
		test.addIbaDy();
		// 查询
		test.queryOne();
		test.login();
		test.queryMore();
		test.queryAll();
		// 更新
		test.updateInfo();
		// 删除
		test.deleteInfo();
	}
}

log4j配置文件log4j.properties:
#log4j.rootLogger = [ level ] , appenderName, appenderName, ...
log4j.rootLogger = INFO, console, R
#level=INFO,all can be output
#console is set to be a ConsoleAppender
log4j.appender.console = org.apache.log4j.ConsoleAppender
#console have four patterns
#org.apache.log4j.HTMLLayout
#org.apache.log4j.PatternLayout
#org.apache.log4j.SimpleLayout
#org.apache.log4j.TTCCLayout
log4j.appender.console.layout = org.apache.log4j.PatternLayout
#define the output type
log4j.appender.console.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [%c]-[%p] %m%n
log4j.logger.com.ibatis=debug
log4j.logger.com.ibatis.common.jdbc.SimpleDataSource=debug
log4j.logger.com.ibatis.common.jdbc.ScriptRunner=debug
log4j.logger.com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate=debug
log4j.logger.java.sql.Connection=debug
log4j.logger.java.sql.Statement=debug
log4j.logger.java.sql.PreparedStatement=debug
#file is set to output to a extra file
log4j.appender.R = org.apache.log4j.RollingFileAppender
#the absolute route of the log4j file
log4j.appender.R.File = /log.txt
#the size
log4j.appender.R.MaxFileSize = 500KB
#back up a file
log4j.appender.R.MaxBackupIndex = 1
log4j.appender.R.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH\:mm\:ss} %5p [%F\:%L] %c{8}.%M() - %m%n

OK!基本就是这样子,ORACLE使用触发器实现主键自增的方法:insert语句中省去ID字段相关的东西,在数据库中创建类似这样的触发器:
create or replace trigger tr_ibid
before insert on ibatislearn
for each row
declare
begin
select ibatis_id.nextval into :new.id from dual;
end tr_ibid;

另外iBatis的n+1问题(一次目的查询在数据库查询了2次,其实就是外键问题,要取到被关联主键的所有信息),此处有详解http://cuishen.iteye.com/blog/544207
ibatis比较容易学习,但写代码的时候要细心,可能少个符号就报一堆错,千万记住!
  • 大小: 42 KB
  • ibatislearn.zip (1 MB)
  • 描述: 项目源码内有ibatis和log4j架包
  • 下载次数: 0
  • 查看图片附件
上一篇: 如何让自己的创业项目“性感”起来 下一篇: 没有下一篇了!
发表评论
用户名: 匿名