SSM框架之---Mybatis的学习笔记

MyBatis学习笔记


Mybatis和hibernate不同,它是Ibatis的进化,它不完全是一个ORM框架,因为MyBatis需要程序员自己编写Sql语句,不过mybatis可以通过XML或注解方式灵活配置要运行的sql语句,并将java对象和sql语句映射生成最终执行的sql,最后将sql执行的结果再映射生成java对象。


Mybatis学习门槛低,简单易学,程序员直接编写原生态sql,可严格控制sql执行性能,灵活度高,非常适合对关系数据模型要求不高的软件开发,例如互联网软件、企业运营类软件等,因为这类软件需求变化频繁,一但需求变化要求成果输出迅速。但是灵活的前提是mybatis无法做到数据库无关性,如果需要实现支持多种数据库的软件则需要自定义多套sql映射文件,工作量大。


Hibernate对象/关系映射能力强,数据库无关性好,对于关系模型要求高的软件(例如需求固定的定制化软件)如果用hibernate开发可以节省很多代码,提高效率。但是Hibernate的学习门槛高,要精通门槛更高,而且怎么设计O/R映射,在性能和对象模型之间如何权衡,以及怎样用好Hibernate需要具有很强的经验和能力才行。


总之,按照用户的需求在有限的资源环境下只要能做出维护性、扩展性良好的软件架构都是好架构,所以框架只有适合才是最好。


我感觉其实就是,不需要经常改动SQL语句的话就用Hibernate,如果要经常改动SQL语句就用Mybatis
Hibernate没那么繁琐(但不灵活,SQL语句被封装了)

Mybatis相对比较繁琐,需要写很多SQL语句,但是很灵活,而且要优化也会比较好点

 


MyBatis和Hibernate
  Mybatis Hibernate
市场占有率
适合的行业 互联网 电商 项目 传统的(ERP CRM OA)
性能
Sql灵活性
门槛
Sql配置文件 多(维护麻烦)
ORM 半自动化 完全的自动化
数据库无关性


使用的表



Mybatis的相关配置文件

sqlConfig.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 resource="db.properties">
	</properties>

	<!-- MyBatis的设置 -->
	<settings>
		<!-- 延时加载开启 -->
		<setting name="lazyLoadingEnabled" value="true"/>
		<!-- 积极加载关闭 -->
		<setting name="aggressiveLazyLoading" value="false"/>
		<setting name="cacheEnabled" value="true"/>
	</settings>

	<!-- 定义别名,这样可以让以后的指定某个类的代码可以减少 -->
	<typeAliases >
		<!-- 批量定义别名,将某个包下的所有类都路径都指定为类名 -->
		<package name="cn.gdufe.po"></package>
		<package name="cn.gdufe.vo"></package>
		<package name="cn.gdufe.pojo"/>
		<package name="cn.gdufe.dao"/>
	</typeAliases>

	<!-- 数据库的相关配置,当引入spring时,就不用写了 -->
	<environments default="development">
		<environment id="development">
			<transactionManager type="JDBC"/>
			<dataSource type="POOLED">
				<property name="driver" value="${jdbc.driver}"/>
				<property name="url" value="${jdbc.url}"/>
				<property name="username" value="${jdbc.username}"/>
				<property name="password" value="${jdbc.password}"/>
			</dataSource>
		</environment>
	</environments>

	<!-- 加载mapper配置文件 -->
	<mappers>

		<!-- 加载单个 -->
		<mapper resource="sqlmap/UserMapper.xml"/>
		<!--  
		<mapper resource="mappers/UserMapper.xml"/>
		<mapper class="cn.gdufe.dao.UserMapper"/>
		-->

		<!-- 批量 -->
		<package name="cn.gdufe.dao"/>
	</mappers>
</configuration>

xxxMapper.xml(这里举个UserMapper的例子)
(User类)

public class User {
	private int uid;
	private String username;
	private String password;
	public int getUid() {
		return uid;
	}
	public void setUid(int uid) {
		this.uid = uid;
	}
	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;
	}
	@Override
	public String toString() {
		return "User [uid=" + uid + ", username=" + username + ", password=" + password + "]";
	}

}

UserMapper.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">

<!-- 对应于UserMapper的配置文件,规范了这些东西,MyBatis就可以自动生成代理对象 -->
<mapper namespace="cn.gdufe.dao.UserMapper">
	<!-- SQL片段,为了重用某段相同的语句 -->
	<sql id="user_query_where">
		<!-- 条件语句,适合条件查找什么的 -->
		<if test="userCustomer!=null">
				<if test= "userCustomer.password!=null and userCustomer.password!=''">
					and user.password=#{userCustomer.password}
				</if> 
				<if test="userCustomer.username!=null and userCustomer.username!=''">
					and user.username like '%${userCustomer.username}%'
				</if>
		</if>
		<if test="uidList!=null">
			<!-- 
				循环语句,循环uidList,每个项为uid,  
				open是指拼接时先加什么语句(这里是"add (") 
				close是拼接后加的字符串
				separator是每个元素之间加的
			 -->
			<foreach collection="uidList" item="uid" open="and (" close=")" separator=" or ">
				uid=#{uid}
			</foreach>
		</if>
	</sql>
	
	<!-- 
		当返回结果类型为resultMap, 配置resultMap
		type:映射到某个类,此处用了别名User,其实就是映射到User类
		id:唯一标识
	-->
	<resultMap type="User" id="UserResultMap">
		<!-- 主键的 -->
		<id column="uid_" property="uid"/>
		<!-- 普通键 -->
		<result column="Username_" property="username"/>
	</resultMap>
	
	<!-- 
		配置一个Statament语句
		parameterType:输入映射参数类型
		resultType:输出映射参数类型
	 -->
	<select id="findUserByUid" parameterType="int" resultType="user">
		select * from user where uid=#{uid}
	</select>
	
	<!-- 输出映射为resultMap -->
	<select id="findUserByUidResultMap" parameterType="int" resultMap="UserResultMap">
		select uid uid_,username username_ from user where uid=#{uid}
	</select>
	
	<select id="findUserList" parameterType="UserPo" resultType="UserCustomer">
		select * from user
		<!-- 自动加where 并把sql片段加进去 -->
		<where>
			<include refid="user_query_where"></include>
		</where>
	</select>
	
	<select id="findUserCount" parameterType="Userpo" resultType="int">
		select count(*) from user 		
		<where>
			<include refid="user_query_where"></include>
		</where>
	</select>
	
	<!-- 模糊查找,但是SQL安全问题好像没有解决,就是SQL注入问题,因为${}是拼接的方式 -->
	<select id="findUserByUsername" parameterType="String" resultType="user">
		select * from user where username like '%${value}%'
	</select>
	
	<!-- 删除语句,没有返回值的语句可以没有输出映射 -->
	<delete id="deleteUser" parameterType="int">
		delete from user where uid=#{uid}
	</delete>
	
	<!-- 更新 -->
	<update id="updateUser" parameterType="user">
		update user set username=#{username},password=#{password} where uid=#{uid}
	</update>
	
	<!-- 添加 -->
	<insert id="insertUser" parameterType="user">
		<!-- 添加完,可以让查到的uid赋值到user中 -->
		<selectKey keyProperty="uid" resultType="int">
			select LAST_INSERT_ID()
		</selectKey>
		insert into user(username,password) values(#{username}, #{password})
	</insert>
</mapper>

log4j.properties(配置日志的,开发时最好是打开debug级别)

log4j.rootLogger=DEBUG,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
log4j.logger.org.apache=INFO

db.properties(配置数据库信息的,独立出来方便数据库的迁移,/应该是转义的原因)

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc\:mysql\://localhost\:3306/databaseName
jdbc.username=username
jdbc.password=password

简单的使用样例

	private SqlSessionFactory sqlSessionFactory = null;
	private String resource = "sqlConfig.xml";
	//测试查找单个User
	@Test
	public void testFindUserByUid() throws Exception {
		//输入流,就是读取sqlConfig.xml的信息
		InputStream inputStream = Resources.getResourceAsStream(resource);
		//创建sqlSessionFactory
		sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
		//得到sqlSession,最好是在方法体内,防止多线程时争抢资源发生问题
		SqlSession sqlSession = sqlSessionFactory.openSession();
		//调用Mapper代理器的方法
		User user = sqlSession.getMapper(UserMapper.class).findUserByUid(12);
		System.out.println(user);
	}
	//测试通过姓名模糊查找
	@Test
	public void testFindUserByUsername() throws Exception {
		InputStream inputStream = Resources.getResourceAsStream(resource);
		sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
		SqlSession sqlSession = sqlSessionFactory.openSession();
		List<User> userList = sqlSession.getMapper(UserMapper.class).findUserByUsername("小明");
		for(User user:userList)
		System.out.println(user);
	}

一个相对复杂一些的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">

<!-- 对应于UserMapper的配置文件,规范了这些东西,MyBatis就可以自动生成代理对象 -->
<mapper namespace="cn.gdufe.dao.Orders_UserMapper">
	
	<!-- 开启二级缓存,并整合ehcache,记得导包
		mybatis-ehcache-1.1.0.jar和ehcache的包(不知道要不要带-core.....忘了)
	 -->
	<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
	<resultMap type="Orders" id="Orders_User">
		<!-- 订单映射 -->
		<id column="id" property="id"/>
		<result column="user_id" property="user_id"/>
		<result column="number" property="number"/>
		<result column="createtime" property="createtime"/>
		<result column="note" property="note"/>
		
		<!-- 关联的对象的属性,用户映射 -->
		<association property="user" javaType="User">
			<id column="username" property="username"/>
			<result column="sex" property="sex"/>
			<result column="address" property="address"/>
		</association>
	</resultMap>
	
	<!-- 继承了Orders_User的resultMap,即不用再写订单,用户的映射关系 -->
	<resultMap id="findOrdersUserOrderdetailResultMap" type="Orders" extends="Orders_User">
		<!-- 订单映射 -->
		<!-- 用户映射 -->
		<!-- 通过继承,可不用写 -->
		<!-- 集合 -->
		<collection property="list" ofType="OrderDetail">
			<id column="orderdetail_id" property="id"/>
			<result column="orders_id" property="orders_id"/>
			<result column="items_id" property="items_id"/>
			<result column="items_num" property="items_num"/>
		</collection>
	</resultMap>
	
	<!-- 延时加载的案例,记得要开启延时记载的设置 -->
	<resultMap id="fingOrderLazyLoadig" type="Orders">
		<id column="id" property="id"/>
		<result column="user_id" property="user_id"/>
		<result column="number" property="number"/>
		<result column="createtime" property="createtime"/>
		<result column="note" property="note"/>
		
		<association property="user" javaType="User" select="cn.gdufe.dao.UserMapper.findUserById" column="user_id">
			
		</association>
	</resultMap>
	
	<!-- 延时加载的案例 -->
	<select id="findOrderLazyLoading" resultMap="fingOrderLazyLoadig">
		select * from orders;
	</select>
	
	<!-- 多对多,一个用户可以购买多个商品 -->
	<resultMap id="findUserOrdersOrderdetailResultMap" type="User">
		<!-- User表信息 -->
		<id column="user_id" property="id"/>
		<result column="username" property="username"/>
		<result column="sex" property="sex"/>
		<result column="address" property="address"/>
		
		<!-- 跟User有关的Order信息 -->
		<collection property="orderList" ofType="Orders">
			<id column="id" property="id"/>
			<result column="user_id" property="user_id"/>
			<result column="number" property="number"/>
			<result column="createtime" property="createtime"/>
			<result column="note" property="note"/>
			
			<!-- Orders中orderDetail列表的信息 -->
			<collection property="list" ofType="OrderDetail">
				<id column="orderdetail_id" property="id"/>
				<result column="orderdetail_items_id" property="items_id"/>
				<result column="orderdetail_items_num" property="items_num"/>
				
				<!-- OrderDetail中对应Items的信息 -->
				<association property="items" javaType="Items">
					<id column="items_id" property="id"/>
					<result column="items_name" property="name"/>
					<result column="items_createtime" property="createtime"/>
					<result column="items_detail" property="detail"/>
					<result column="items_pic" property="pic"/>
					<result column="items_price" property="price"/>
				</association>
			</collection>
		</collection>
		
	</resultMap>
	
	<!-- 多对多的案例 查询所有有过订单的用户的商品详细信息-->
	<select id="findUserOrdersOrderdetailResultMap" resultMap="findUserOrdersOrderdetailResultMap">
SELECT orders.*,
user.username,user.sex,user.address,
orderdetail.`id` orderdetail_id,orderdetail.`items_id` orderdetail_items_id,orderdetail.`items_num` orderdetail_items_num,orderdetail.`orders_id`,
items.`id` items_id, items.`name` items_name,items.`createtime` items_createtime,items.`detail` items_detail,items.pic items_pic,items.`price` items_price
 FROM orders,USER,orderdetail,items WHERE orders.`user_id`=user.`id` AND orders.`id`=orderdetail.orders_id AND orderdetail.items_id = items.`id`	
 	</select>
	
	<select id="findOrdersUser" resultType="Orders_User">
		select orders.*,user.username,user.address from orders,user where orders.user_id = user.id
	</select>
	
	<select id="findOrdersUserResultMap" resultMap="Orders_User">
		select orders.*,user.username,user.sex,user.address from orders,user where orders.user_id = user.id
	</select>
	
	<!-- 测试一对多的案例 -->
	<select id="findOrdersUserOrderdetailResultMap" resultMap="findOrdersUserOrderdetailResultMap">
		SELECT orders.*,user.username,user.sex,user.address,orderdetail.`id` orderdetail_id,orderdetail.`items_id`,
orderdetail.`items_num`,orderdetail.`orders_id` FROM orders,USER,orderdetail WHERE orders.`user_id`=user.`id` AND orders.`id`=orderdetail.orders_id;
	</select>
</mapper>

所用到的表关系(具体的表数据就不截出来了)


Mybatis和Spring整合:


注意问题:一定到记得导入相应的包,核心是mybatis-spring-1.3.2.jar,还有用到的各种数据库包,这里用了c3p0


spring和mybatis整合时,一定要导入下面这2个包:
spring-tx-4.3.13.RELEASE.jar,没导的话extends SqlSessionDaoSupport时会有问题(dao接口)


spring-jdbc-4.3.13.RELEASE.jar,没导的话,配置中的dataSource会出现异常,导致sqlSessionFactory创建不成功
不然会报错.......


各种配置文件
sqlConfig.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="cn.gdufe.ssm.po"></package>
	</typeAliases>
	
	<!-- 加载映射 -->
	<mappers>
		<mapper resource="sqlmap/UserMapper.xml"/>
		<!--  
		<mapper resource="mappers/UserMapper.xml"/>
		<mapper class="cn.gdufe.dao.UserMapper"/>
		-->
		<!-- 批量,如果spring已经做了映射,那么这个mappers标签也不用写了 -->
		<package name="cn.gdufe.ssm.mapper"/>
	</mappers>
</configuration>

applicationContext.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:mvc="http://www.springframework.org/schema/mvc"
    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/mvc  
                        http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">

	<!-- 加载数据库的属性 -->
    <context:property-placeholder location="classpath:db.properties" />

	<!-- 配置连接池参数就是在上面的db.properties里 -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
		<property name="driverClass" value="${jdbc.driver}"></property>
		<property name="jdbcUrl" value="${jdbc.url}"></property>
		<property name="user" value="${jdbc.username}"></property>
		<property name="password" value="${jdbc.password}"></property>
	</bean>
	
	<!-- 配置sqlSessionFactory -->
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<!-- 加载mybatis的配置文件 -->
		<property name="configLocation" value="mybatis/sqlConfig.xml"></property>
		<property name="dataSource" ref="dataSource"/>
	</bean>
	
	<!-- 配置userDao,原始方式 -->
	<bean id="userDao" class="cn.gdufe.ssm.dao.UserDaoImpl">
		<property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
	</bean>
	
	<!-- 配置Mapper,class填代理对象org.mybatis.spring.mapper.MapperFactoryBean -->
<!-- 	<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
		配置接口
		<property name="mapperInterface" value="cn.gdufe.ssm.mapper.UserMapper" />
		配置sqlSessionFactory
		<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
	</bean> -->
	
	<!-- 
		一次性配置多个Mapper,写完这个就可以不用在sqlConfig.xml中加载Mapper了
		但是,配置文件要和相应的mapper类要在同一个目录,而且namespace要等于相应类的路径
		自动扫描之后,Bean的id就是Mapper的类名,首字母小写
	-->
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<!-- 指定扫描的包名 -->
		<property name="basePackage" value="cn.gdufe.ssm.mapper"/>
		<!-- 可以继续添加包,用半圆角逗号分隔,比如cn.gdufe.ssm.mapper1,cn.gdufe.ssm.mapper2	 -->
		<!-- 注入sqlSessionFactoryBeanName,为了让数据源先执行 -->
		<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
	</bean>
	
</beans>


使用案例

private ApplicationContext applicationContext;
	
	//会在Test之前运行
	@Before
	public void setUp() throws Exception {
		applicationContext = new ClassPathXmlApplicationContext("classpath:spring/applicationContext.xml");

	}
	
	//测试使用Mapper代理
	@Test
	public void test() throws Exception {
		UserMapper userMapper = (UserMapper) applicationContext.getBean("userMapper");
		User user = userMapper.findUserById(1);
		System.out.println(user);
		
	}

MyBatis的逆向工程


核心包:mybatis-generator-core-1.3.6.jar


配置文件generatorConfig.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
  PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
  "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
    <context id="testTables" targetRuntime="MyBatis3">
        <commentGenerator>
            <!-- 是否去除自动生成的注释 true:是 : false:否 -->
            <property name="suppressAllComments" value="true" />
        </commentGenerator>
        <!--数据库连接的信息:驱动类、连接地址、用户名、密码 -->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
            connectionURL="jdbc:mysql://localhost:3306/test1" userId="root"
            password="123456">
        </jdbcConnection>
        <!-- oracle数据库的 -->
        <!-- <jdbcConnection driverClass="oracle.jdbc.OracleDriver"
            connectionURL="jdbc:oracle:thin:@127.0.0.1:1521:yycg" 
            userId="yycg"
            password="yycg">
        </jdbcConnection> -->

        <!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL 和 
            NUMERIC 类型解析为java.math.BigDecimal -->
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false" />
        </javaTypeResolver>

        <!-- targetProject:生成PO类的位置 -->
        <javaModelGenerator targetPackage="cn.gdufe.po"
            targetProject=".\src">
            <!-- enableSubPackages:是否让schema作为包的后缀 -->
            <property name="enableSubPackages" value="false" />
            <!-- 从数据库返回的值被清理前后的空格 -->
            <property name="trimStrings" value="true" />
        </javaModelGenerator>
        <!-- targetProject:mapper映射文件生成的位置 -->
        <sqlMapGenerator targetPackage="cn.gdufe.mapper" 
            targetProject=".\src">
            <!-- enableSubPackages:是否让schema作为包的后缀 -->
            <property name="enableSubPackages" value="false" />
        </sqlMapGenerator>
        <!-- targetPackage:mapper接口生成的位置 -->
        <javaClientGenerator type="XMLMAPPER"
            targetPackage="cn.gdufe.mapper" 
            targetProject=".\src">
            <!-- enableSubPackages:是否让schema作为包的后缀 -->
            <property name="enableSubPackages" value="false" />
        </javaClientGenerator>
        <!-- 指定数据库表,schema可以不写 -->
        <table schema="" tableName="user"></table>
        <table schema="" tableName="orders"></table>
				<table tableName="items"></table>
				<table tableName="orderDetail"></table>
      <!-- 有些表的字段需要指定java类型
         <table schema="" tableName="">
            <columnOverride column="" javaType="" />
        </table> -->
    </context>
</generatorConfiguration>

运行代码GeneratorSqlMap.java

public class GeneratorSqlMap {

    public void generator() throws Exception{

        List<String> warnings = new ArrayList<String>();
        boolean overwrite = true;
        //指定 逆向工程配置文件
        File configFile = new File("generatorConfig.xml"); 
        ConfigurationParser cp = new ConfigurationParser(warnings);
        Configuration config = cp.parseConfiguration(configFile);
        DefaultShellCallback callback = new DefaultShellCallback(overwrite);
        MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config,
                callback, warnings);
        myBatisGenerator.generate(null);

    } 
    
    public static void main(String[] args) throws Exception {
        try {
        	GeneratorSqlMap generatorSqlmap = new GeneratorSqlMap();
            generatorSqlmap.generator();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}


完成,逆向工程自动帮我们创建了相应的mapper.java,mapper.xml,以及po(持久层的bean)类



最后,就可以使用MyBatis给我们写的一些非常常用的一些SQL语句了

猜你喜欢

转载自blog.csdn.net/qq_31281327/article/details/79628583