MyBatis随笔
前一阵参与了一个项目的搭建,为了快速开发再加上学一些新东西,准备采用React+Spring MVC+MyBatis的架构。
花了一些时间最终把Spring MVC+MyBatis打通。
这里总结下MyBatis的使用过程,方便以后查阅。
参考资料:
1. http://www.mybatis.org/mybatis-3/zh/
2. http://blog.csdn.net/isea533/article/category/2092001
不玩儿虚的,直接上!
环境:Intellij IDEA 12; SQLSERVER;
既然是Spring MVC+MyBatis的架构,分service和persistence层。另外,Intellij IDEA构建多模块结构还是挺方便的,两个模块如下:
整体的运作就是service层调用persistence层的dao来访问数据库,而MyBatis则作为数据库SQLSERVER同service层的桥梁。
MyBatis整体来说,同Hibernate一样,是对JDBC的轻量级封装,完成ORM功能。
从使用MyBatis和Hibernate来说,个人感觉,Hibernate更加偏向于对象化,比如Criteria和Query,创建查询对象;MyBatis则偏向于sql,将sql语句写入mapper文件中。
总体来说,使用Hibernate需要多多少少了解它的HQL语句,而MyBatis则更注重数据库操作的专一化,即只是使用sql语句,对查询结果用resultMap封装,所以难易程度上,个人感觉MyBatis更容易一些。
下面试着打通MyBatis和SQLSERVER......
1. 任何开源框架的使用,肯定是要先进行配置的。
1.1 对MyBatis来说,针对它自己的配置文件是mybatisConfig.xml(默认)。在这个项目里,因为和Spring整合,就只用到日志的配置。
mybatisConfig.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> <settings> <setting name="logImpl" value="SLF4J"/> </settings> </configuration>
这里对应logImpl的value可以从源码里找到(org.apache.ibatis.session.Configuration, 该类配置了很多MyBatis的默认值),如下:
typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);
1.2 因为同Spring整合,添加mybatis-spring依赖(maven方式)
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.0</version> </dependency>
1.3 回到数据库一端,涉及创建数据源以及基于Spring的事务管理
persistence.properties
# jdbc.* jdbc.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver jdbc.url=jdbc:sqlserver://dbhost:dbport;databaseName=db-name; jdbc.user=aaa jdbc.password=aaa jdbc.initialSize=1 jdbc.minIdle=1 jdbc.maxActive=20 jdbc.maxWait=60000 jdbc.tberm=60000 jdbc.meitm=300000 jdbc.vq=SELECT 'x' jdbc.twi=true jdbc.tob=false jdbc.tor=false
persistenceContext.xml (Part I)
<context:property-placeholder location="classpath:persistence.properties"/> <!-- DataSource related --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.user}"/> <property name="password" value="${jdbc.password}"/> <property name="initialSize" value="${jdbc.initialSize}"/> <property name="minIdle" value="${jdbc.minIdle}"/> <property name="maxActive" value="${jdbc.maxActive}"/> <property name="maxWait" value="${jdbc.maxWait}"/> <property name="timeBetweenEvictionRunsMillis" value="${jdbc.tberm}"/> <property name="minEvictableIdleTimeMillis" value="${jdbc.meitm}"/> <property name="validationQuery" value="${jdbc.vq}"/> <property name="testWhileIdle" value="${jdbc.twi}"/> <property name="testOnBorrow" value="${jdbc.tob}"/> <property name="testOnReturn" value="${jdbc.tor}"/> </bean> <!-- 配置Spring的事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <tx:annotation-driven transaction-manager="transactionManager" />
这里的jdbc.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver,SQLServerDriver并没有在Maven Repository托管,而且鉴于项目基于Maven构建,将下载到的sqljdbc41.jar(jre7) install到本地,使用如下命令:
mvn install:install-file -Dfile=sqljdbc41.jar -Dpackaging=jar -DgroupId=com.microsoft.sqlserver -DartifactId=sqljdbc -Dversion=4.1
OK, 接下来是mybatis同Spring整合时的核心部分。
1.4 Mybatis同Spring整合
persistenceContext.xml (Part II)
<!-- 配置sqlSessionFactory --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="mapperLocations" value="classpath:mapper/*.xml"/> <property name="configLocation" value="classpath:mybatisConfig.xml"/> <property name="typeAliasesPackage" value="com.chris.persistence.entity"/> </bean> <!-- 配置扫描器 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.chris.persistence.dao"/> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> </bean>
利用Intellij IDEA,可以看到mapperLocations对应的value呈红色,因为在resources包下并没有mapper文件夹和xml文件。
这个时候应该停下来想想MyBatis怎么做到ORM? 总体来说,同Hibernate类似
1. 数据库表中的记录同java bean对应
Record:
Java Bean:
import java.util.Date; public class Report { private Integer id; private String name; private String description; private String owner; private String customer; private Date created; private String createdby; private Date updated; private String updatedby; private Integer version; private Integer clientversion; public Report(Integer id, String name, String description, String owner, String customer, Date created, String createdby, Date updated, String updatedby, Integer version, Integer clientversion) { this.id = id; this.name = name; this.description = description; this.owner = owner; this.customer = customer; this.created = created; this.createdby = createdby; this.updated = updated; this.updatedby = updatedby; this.version = version; this.clientversion = clientversion; } public Report() { super(); } setter; getter...
2. 程序调用接口方法并根据mapper文件执行sql语句
dao层:
import com.chris.persistence.entity.Report; import com.chris.persistence.entity.SubReport; import java.util.List; public interface ReportMapper { int deleteByPrimaryKey(Integer id); int insert(Report record); int insertSelective(Report record); Report selectByPrimaryKey(Integer id); int updateByPrimaryKeySelective(Report record); int updateByPrimaryKey(Report record); List<SubReport> selectReportListByPWID(String pwid); }
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" > <mapper namespace="com.chris.persistence.dao.ReportMapper"> <resultMap id="BaseResultMap" type="com.chris.persistence.entity.Report"> <constructor> <idArg column="Id" jdbcType="INTEGER" javaType="java.lang.Integer"/> <arg column="Name" jdbcType="VARCHAR" javaType="java.lang.String"/> <arg column="Description" jdbcType="VARCHAR" javaType="java.lang.String"/> <arg column="Owner" jdbcType="CHAR" javaType="java.lang.String"/> <arg column="Customer" jdbcType="VARCHAR" javaType="java.lang.String"/> <arg column="Created" jdbcType="TIMESTAMP" javaType="java.util.Date"/> <arg column="CreatedBy" jdbcType="VARCHAR" javaType="java.lang.String"/> <arg column="Updated" jdbcType="TIMESTAMP" javaType="java.util.Date"/> <arg column="UpdatedBy" jdbcType="VARCHAR" javaType="java.lang.String"/> <arg column="Version" jdbcType="INTEGER" javaType="java.lang.Integer"/> <arg column="ClientVersion" jdbcType="INTEGER" javaType="java.lang.Integer"/> </constructor> </resultMap> <resultMap id="ReportListResultMap" type="SubReport"> <result column="Id" property="id"/> <result column="Name" property="name"/> <result column="Description" property="description"/> </resultMap> <sql id="Base_Column_List"> Id, Name, Description, Owner, Customer, Created, CreatedBy, Updated, UpdatedBy, Version, ClientVersion </sql> <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer"> select <include refid="Base_Column_List"/> from [Report] where Id = #{id,jdbcType=INTEGER} </select> <select id="selectReportListByPWID" parameterType="String" resultMap="ReportListResultMap"> select r.[Id], r.[Name], r.[Description] from [Report] as r left join [Report_Access] as ra on r.[Id]=ra.[ReportId] left join [User] as u on u.[Id]=ra.[UserId] where u.[PWID] = #{pwid} </select> <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer"> delete from [Report] where Id = #{id,jdbcType=INTEGER} </delete> <insert id="insert" parameterType="com.chris.persistence.entity.Report"> insert into [Report] (Id, Name, Description, Owner, Customer, Created, CreatedBy, Updated, UpdatedBy, Version, ClientVersion) values (#{id,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR}, #{description,jdbcType=VARCHAR}, #{owner,jdbcType=CHAR}, #{customer,jdbcType=VARCHAR}, #{created,jdbcType=TIMESTAMP}, #{createdby,jdbcType=VARCHAR}, #{updated,jdbcType=TIMESTAMP}, #{updatedby,jdbcType=VARCHAR}, #{version,jdbcType=INTEGER}, #{clientversion,jdbcType=INTEGER}) </insert> <insert id="insertSelective" parameterType="com.chris.persistence.entity.Report"> insert into [Report] <trim prefix="(" suffix=")" suffixOverrides=","> <if test="id != null"> Id, </if> <if test="name != null"> Name, </if> <if test="description != null"> Description, </if> <if test="owner != null"> Owner, </if> <if test="customer != null"> Customer, </if> <if test="created != null"> Created, </if> <if test="createdby != null"> CreatedBy, </if> <if test="updated != null"> Updated, </if> <if test="updatedby != null"> UpdatedBy, </if> <if test="version != null"> Version, </if> <if test="clientversion != null"> ClientVersion, </if> </trim> <trim prefix="values (" suffix=")" suffixOverrides=","> <if test="id != null"> #{id,jdbcType=INTEGER}, </if> <if test="name != null"> #{name,jdbcType=VARCHAR}, </if> <if test="description != null"> #{description,jdbcType=VARCHAR}, </if> <if test="owner != null"> #{owner,jdbcType=CHAR}, </if> <if test="customer != null"> #{customer,jdbcType=VARCHAR}, </if> <if test="created != null"> #{created,jdbcType=TIMESTAMP}, </if> <if test="createdby != null"> #{createdby,jdbcType=VARCHAR}, </if> <if test="updated != null"> #{updated,jdbcType=TIMESTAMP}, </if> <if test="updatedby != null"> #{updatedby,jdbcType=VARCHAR}, </if> <if test="version != null"> #{version,jdbcType=INTEGER}, </if> <if test="clientversion != null"> #{clientversion,jdbcType=INTEGER}, </if> </trim> </insert> <update id="updateByPrimaryKeySelective" parameterType="com.chris.persistence.entity.Report"> update [Report] <set> <if test="name != null"> Name = #{name,jdbcType=VARCHAR}, </if> <if test="description != null"> Description = #{description,jdbcType=VARCHAR}, </if> <if test="owner != null"> Owner = #{owner,jdbcType=CHAR}, </if> <if test="customer != null"> Customer = #{customer,jdbcType=VARCHAR}, </if> <if test="created != null"> Created = #{created,jdbcType=TIMESTAMP}, </if> <if test="createdby != null"> CreatedBy = #{createdby,jdbcType=VARCHAR}, </if> <if test="updated != null"> Updated = #{updated,jdbcType=TIMESTAMP}, </if> <if test="updatedby != null"> UpdatedBy = #{updatedby,jdbcType=VARCHAR}, </if> <if test="version != null"> Version = #{version,jdbcType=INTEGER}, </if> <if test="clientversion != null"> ClientVersion = #{clientversion,jdbcType=INTEGER}, </if> </set> where Id = #{id,jdbcType=INTEGER} </update> <update id="updateByPrimaryKey" parameterType="com.chris.persistence.entity.Report"> update [Report] set Name = #{name,jdbcType=VARCHAR}, Description = #{description,jdbcType=VARCHAR}, Owner = #{owner,jdbcType=CHAR}, Customer = #{customer,jdbcType=VARCHAR}, Created = #{created,jdbcType=TIMESTAMP}, CreatedBy = #{createdby,jdbcType=VARCHAR}, Updated = #{updated,jdbcType=TIMESTAMP}, UpdatedBy = #{updatedby,jdbcType=VARCHAR}, Version = #{version,jdbcType=INTEGER}, ClientVersion = #{clientversion,jdbcType=INTEGER} where Id = #{id,jdbcType=INTEGER} </update> </mapper>
OK, 写到这里应该明白MyBatis的最核心部分是mapper.xml。
对接触MyBatis时间不长的人来说,最快的办法是有个现成的mapper.xml在那可供参考,然后慢慢熟悉mapper.xml的语法和写法,而不是详细的阅读mapper.xml如何写。
有个mybatis generator工具,可以用来帮助生成mapper.xml。
添加maven依赖:
<dependency> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-core</artifactId> <version>1.3.5</version> </dependency>
添加对应的配置文件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> <properties resource="persistence.properties"/> <context id="default" targetRuntime="MyBatis3"> <commentGenerator> <property name="suppressDate" value="true"/> <property name="suppressAllComments" value="true"/> </commentGenerator> <jdbcConnection driverClass="${jdbc.driverClassName}" connectionURL="${jdbc.url}" userId="${jdbc.user}" password="${jdbc.password}"> </jdbcConnection> <javaTypeResolver> <property name="forceBigDecimals" value="false"/> </javaTypeResolver> <javaModelGenerator targetPackage="com.chris.persistence.entity" targetProject="src/main/java"> <property name="enableSubPackages" value="false"/> <property name="constructorBased" value="true"/> <property name="trimStrings" value="true"/> <property name="immutable" value="false"/> </javaModelGenerator> <sqlMapGenerator targetPackage="mapper" targetProject="src/main/resources"> <property name="enableSubPackages" value="false"/> </sqlMapGenerator> <javaClientGenerator targetPackage="com.chris.persistence.dao" targetProject="src/main/java" type="XMLMAPPER"> <property name="enableSubPackages" value="true"/> </javaClientGenerator> <table tableName="User" domainObjectName="User" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"> </table> <table tableName="Resource" domainObjectName="Resource" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"> </table> <table tableName="Version" domainObjectName="Version" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"> </table> <table tableName="Report" domainObjectName="Report" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"> </table> <table tableName="Report_Access" domainObjectName="Report_Access" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"> </table> </context> </generatorConfiguration>
用maven build, mybatis-generator:generate -e
之后生成dao和entity
在使用mybatis generator工具时,每次运行mybatis-generator:generate -e,dao和entity会被覆盖,但mapper.xml则是追加的方式被修改,所以注意在mybatis-generator之前删除mapper.xml文件。
OK, 总的来说,在使用MyBatis做ORM框架时,dao作为entity和DB record的桥梁,而关于java bean和database row的mapping信息则通过mapper.xml获得。
在MyBatis中,sql写在mapper.xml文件中,而且重点在于如何利用好resultMap和动态sql。
内容总结:
(1) MyBatis配置; (2) MyBatis与Spring整合; (3) Mybatis generator使用
Done!