spring和mybatis集成,自动生成model、mapper,增加mybatis分页功能
软件简介
Spring是一个流行的控制反转(IoC)和面向切面(AOP)的容器框架,在java webapp开发中使用广泛。http://projects.spring.io/spring-framework/
MyBatis是一个基于Java的数据持久层框架,其原名是iBatis,在升级到3.0版本后,更名为MyBatis。https://github.com/mybatis/mybatis-3/
MyBatis Generator是一个MyBatis的代码生成器,通过配置,可自动生成数据操作接口、实体类以及mapper.xml文件。https://github.com/mybatis/generator
maven开发环境搭建
使用eclipseIDE,新建maven工程。
在pom.xml文件中,添加如下内容,引入相关jar。
spring版本是3.1.0,mybatis版本是3.4.0,mybatis-generator版本是1.3.5。
<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.0</modelVersion> <groupId>com.founder</groupId> <artifactId>datacenter</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>datacenter</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <fasterxml.jackson.version>2.1.1</fasterxml.jackson.version> <spring.version>3.1.0.RELEASE</spring.version> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit-dep</artifactId> <version>4.9</version> <scope>test</scope> </dependency> <dependency> <groupId>com.google.protobuf</groupId> <artifactId>protobuf-java</artifactId> <version>2.5.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>${fasterxml.jackson.version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>${fasterxml.jackson.version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>${fasterxml.jackson.version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-xml</artifactId> <version>${fasterxml.jackson.version}</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.15</version> </dependency> <dependency> <groupId>org.jdom</groupId> <artifactId>jdom</artifactId> <version>1.1.3</version> </dependency> <dependency> <groupId>org.dom4j</groupId> <artifactId>dom4j</artifactId> <version>2.0.0</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${spring.version}</version> </dependency> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.39</version> </dependency> <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.0</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.0</version> </dependency> <!-- https://mvnrepository.com/artifact/org.mybatis.generator/mybatis-generator --> <dependency> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator</artifactId> <version>1.3.5</version> <type>pom</type> </dependency> <!-- https://mvnrepository.com/artifact/org.mybatis.generator/mybatis-generator-core --> <dependency> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-core</artifactId> <version>1.3.5</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.7</source> <target>1.7</target> </configuration> </plugin> </plugins> </build> </project>
工程目录结构
数据库建表
CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, `password` varchar(255) DEFAULT NULL, `lastLoginTime` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=22 DEFAULT CHARSET=utf8;
生成mybatis的代码
为了生成mybatis的代码,首先需要创建一个配置文件,告诉mybatis generator必须的变量。
配置文件保存在src/main/conf/build-mybatis.xml中。
具体配置信息参考官网http://www.mybatis.org/generator/index.html。
注意,配置文件中,添加了一个plugin,这是为生成分页操作添加的,具体内容,后面会讲解。
table中的tableName设置为%,意味着为mysql数据库中的所有表生成对应的代码文件。
mysql中表明使用“_”或者“-”分隔,自动生成的代码文件名中会去掉,并且其后面的字母会升级为大写。
指定好生成的代码文件的保存地址,共有三个。
<?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> <!--数据库驱动 --> <classPathEntry location="C:\Users\.m2\repository\mysql\mysql-connector-java\5.1.39\mysql-connector-java-5.1.39.jar" /> <context id="MySQL2Tables" targetRuntime="MyBatis3" defaultModelType="flat"> <plugin type="com.founder.datacenter.utils.mybatis.MySQLPaginationPlugin" /> <commentGenerator> <property name="suppressDate" value="true" /> <property name="suppressAllComments" value="true" /> </commentGenerator> <!--数据库链接地址账号密码 --> <jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/mybatis?useSSL=false" userId="root" password="123456"> </jdbcConnection> <javaTypeResolver> <property name="forceBigDecimals" value="false" /> </javaTypeResolver> <!--生成Model类存放位置 --> <javaModelGenerator targetPackage="com.founder.datacenter.model" targetProject="src/main/java"> <property name="enableSubPackages" value="true" /> <property name="trimStrings" value="true" /> </javaModelGenerator> <!--生成映射文件存放位置 --> <sqlMapGenerator targetPackage="com.founder.datacenter.dao.mapper" targetProject="src/main/java"> <property name="enableSubPackages" value="true" /> </sqlMapGenerator> <!--生成Dao类存放位置 --> <javaClientGenerator type="XMLMAPPER" targetPackage="com.founder.datacenter.dao.mapper" targetProject="src/main/java"> <property name="enableSubPackages" value="true" /> </javaClientGenerator> <!--生成对应表及类名 --> <table tableName="%" enableCountByExample="true" enableUpdateByExample="true" enableDeleteByExample="true" enableSelectByExample="true" selectByExampleQueryId="false"></table> </context> </generatorConfiguration>
编写代码生成脚本
package com.founder.datacenter.utils.mybatis; import java.io.File; import java.io.IOException; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import org.mybatis.generator.api.MyBatisGenerator; import org.mybatis.generator.config.Configuration; import org.mybatis.generator.config.xml.ConfigurationParser; import org.mybatis.generator.exception.InvalidConfigurationException; import org.mybatis.generator.exception.XMLParserException; import org.mybatis.generator.internal.DefaultShellCallback; public class MyBatisGeneratorTool { public static void main(String[] args) { List<String> warnings = new ArrayList<String>(); boolean overwrite = true; String genCfg = "/build-mybatis.xml"; File configFile = new File(MyBatisGeneratorTool.class.getResource(genCfg).getFile()); ConfigurationParser cp = new ConfigurationParser(warnings); Configuration config = null; try { config = cp.parseConfiguration(configFile); } catch (IOException e) { e.printStackTrace(); } catch (XMLParserException e) { e.printStackTrace(); } DefaultShellCallback callback = new DefaultShellCallback(overwrite); MyBatisGenerator myBatisGenerator = null; try { myBatisGenerator = new MyBatisGenerator(config, callback, warnings); } catch (InvalidConfigurationException e) { e.printStackTrace(); } try { myBatisGenerator.generate(null); } catch (SQLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } }
脚本运行后,会在指定的目录生成代码文件。
测试生成的代码
首先在src/main/conf目录下添加配置文件,其中包括jsbc.properties的配置文件,mybatis-spring的配置文件,以及service bean的配置文件。
spring相关的配置文件放置在src/main/conf/spring目录下。
jdbc.properties
jdbc.driverClassName:com.mysql.jdbc.Driver jdbc.url:jdbc:mysql://localhost:3306/mybatis?useSSL=false jdbc.username:root jdbc.password:123456
spring-mybatis.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:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jee="http://www.springframework.org/schema/jee" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd"> <!-- 引入dbconfig.properties属性文件 --> <context:property-placeholder location="classpath:jdbc.properties" /> <!-- ========================================配置数据源========================================= --> <!-- 配置数据源 --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName"> <value>${jdbc.driverClassName}</value> </property> <property name="url"> <value>${jdbc.url}</value> </property> <property name="username"> <value>${jdbc.username}</value> </property> <property name="password"> <value>${jdbc.password}</value> </property> </bean> <!-- 自动扫描目录下所有SQL映射的xml文件, --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="mapperLocations" value="classpath:com/founder/datacenter/dao/mapper/*.xml" /> </bean> <!-- 扫描指定包以及子包下的所有映射接口类 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" /> <property name="basePackage" value="com.founder.datacenter.dao.mapper" /> </bean> <!-- 配置Spring的事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <!-- 注解方式配置事物 --> <tx:annotation-driven transaction-manager="transactionManager" /> </beans>
spring-service.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:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:jee="http://www.springframework.org/schema/jee" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd">
<!--其实component-scan 就有了annotation-config的功能即把需要的类注册到了spring容器中 --> <context:component-scan base-package="com.founder.datacenter.service" /> <!-- 可以使用@Service注解,完成service的bean声明,不必在spring文件中添加bean --> </beans>
ExampleService.java
package com.founder.datacenter.service; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.founder.datacenter.dao.mapper.UserMapper; import com.founder.datacenter.model.User; import com.founder.datacenter.model.UserExample; @Service("ExampleService") public class ExampleService { @Autowired private UserMapper userMapper; public boolean existUser(String name) { UserExample userE = new UserExample(); userE.createCriteria().andNameEqualTo(name); userE.setDistinct(true); List<User> user = userMapper.selectByExample(userE); if (user.size() > 0) return true; else return false; } }
测试代码
package com.founder.datacenter; import static org.junit.Assert.assertEquals; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.BeanFactory; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.founder.datacenter.service.ExampleService; public class AppTest { BeanFactory factory; @Before public void before() { factory = new ClassPathXmlApplicationContext("spring/spring-*.xml"); System.out.println("@Before"); } @After public void after() { System.out.println("@After"); } @Test public void exitTest() { ExampleService service = factory.getBean("ExampleService", ExampleService.class); assertEquals(service.existUser("mahuan2"), true); } }
mysql表中插入了一条name为mahuan2的数据。
测试结果通过。
分页功能添加
原始生成的代码是无法完成分页功能的,因为需要添加自定义插件,增加分页功能。
插件代码
package com.founder.ebd.util.mybatis; import java.util.List; import org.mybatis.generator.api.CommentGenerator; import org.mybatis.generator.api.IntrospectedColumn; import org.mybatis.generator.api.IntrospectedTable; import org.mybatis.generator.api.Plugin; import org.mybatis.generator.api.PluginAdapter; import org.mybatis.generator.api.dom.java.Field; import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType; import org.mybatis.generator.api.dom.java.JavaVisibility; import org.mybatis.generator.api.dom.java.Method; import org.mybatis.generator.api.dom.java.Parameter; import org.mybatis.generator.api.dom.java.TopLevelClass; import org.mybatis.generator.api.dom.xml.Attribute; import org.mybatis.generator.api.dom.xml.TextElement; import org.mybatis.generator.api.dom.xml.XmlElement; public class MySQLPaginationPlugin extends PluginAdapter { @Override public boolean modelExampleClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) { // add field, getter, setter for limit clause addLimit(topLevelClass, introspectedTable, "limitStart"); addLimit(topLevelClass, introspectedTable, "count"); // add the method that get the only Criteria addCriteriaGetter(topLevelClass, introspectedTable); return super.modelExampleClassGenerated(topLevelClass, introspectedTable); } @Override public boolean sqlMapSelectByExampleWithoutBLOBsElementGenerated(XmlElement element, IntrospectedTable introspectedTable) { XmlElement isNotNullElement = new XmlElement("if"); //$NON-NLS-1$ isNotNullElement.addAttribute(new Attribute("test", "limitStart != null and limitStart >= 0")); //$NON-NLS-1$ //$NON-NLS-2$ isNotNullElement.addElement(new TextElement("limit ${limitStart} , ${count}")); element.addElement(isNotNullElement); return super.sqlMapUpdateByExampleWithoutBLOBsElementGenerated(element, introspectedTable); } @Override public boolean sqlMapSelectByExampleWithBLOBsElementGenerated(XmlElement element, IntrospectedTable introspectedTable) { XmlElement isNotNullElement = new XmlElement("if"); //$NON-NLS-1$ isNotNullElement.addAttribute(new Attribute("test", "limitStart != null and limitStart >= 0")); //$NON-NLS-1$ //$NON-NLS-2$ isNotNullElement.addElement(new TextElement("limit ${limitStart} , ${count}")); element.addElement(isNotNullElement); return super.sqlMapUpdateByExampleWithoutBLOBsElementGenerated(element, introspectedTable); } @Override public boolean modelGetterMethodGenerated(Method method, TopLevelClass topLevelClass, IntrospectedColumn introspectedColumn, IntrospectedTable introspectedTable, Plugin.ModelClassType modelClassType) { return super.modelGetterMethodGenerated(method, topLevelClass, introspectedColumn, introspectedTable, modelClassType); } private void addLimit(TopLevelClass topLevelClass, IntrospectedTable introspectedTable, String name) { CommentGenerator commentGenerator = context.getCommentGenerator(); Field field = new Field(); field.setVisibility(JavaVisibility.PROTECTED); field.setType(FullyQualifiedJavaType.getIntInstance()); field.setName(name); field.setInitializationString("-1"); commentGenerator.addFieldComment(field, introspectedTable); topLevelClass.addField(field); char c = name.charAt(0); String camel = Character.toUpperCase(c) + name.substring(1); Method method = new Method(); method.setVisibility(JavaVisibility.PUBLIC); method.setName("set" + camel); method.addParameter(new Parameter(FullyQualifiedJavaType.getIntInstance(), name)); method.addBodyLine("this." + name + "=" + name + ";"); commentGenerator.addGeneralMethodComment(method, introspectedTable); topLevelClass.addMethod(method); method = new Method(); method.setVisibility(JavaVisibility.PUBLIC); method.setReturnType(FullyQualifiedJavaType.getIntInstance()); method.setName("get" + camel); method.addBodyLine("return " + name + ";"); commentGenerator.addGeneralMethodComment(method, introspectedTable); topLevelClass.addMethod(method); } private void addCriteriaGetter(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) { CommentGenerator commentGenerator = context.getCommentGenerator(); Method method = new Method(); method.setVisibility(JavaVisibility.PUBLIC); method.setName("getCriteria"); method.setReturnType(new FullyQualifiedJavaType("Criteria")); method.addBodyLine("if (oredCriteria.size() != 0) {return oredCriteria.get(0);}"); method.addBodyLine("Criteria criteria = createCriteriaInternal();"); method.addBodyLine("oredCriteria.add(criteria);"); method.addBodyLine("return criteria;"); commentGenerator.addGeneralMethodComment(method, introspectedTable); topLevelClass.addMethod(method); } @Override public boolean validate(List<String> arg0) { // TODO Auto-generated method stub return true; } }
mysql的分页形式为limit startIndex,count的形式。
在build-mybatis.xml中增加插件信息,在生成代码后,便可以使用分页功能了。
生成代码中,UserExample增加了两个变量,UserMapper.xml相应的增加了limit的语句。
使用分页的代码
package com.founder.datacenter.service; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.founder.datacenter.dao.mapper.UserMapper; import com.founder.datacenter.model.User; import com.founder.datacenter.model.UserExample; @Service("ExampleService") public class ExampleService { @Autowired private UserMapper userMapper; public boolean existUser(String name) { UserExample userE = new UserExample(); userE.createCriteria().andNameEqualTo(name); userE.setLimitStart(0); userE.setCount(1); userE.setDistinct(true); List<User> user = userMapper.selectByExample(userE); if (user.size() > 0) return true; else return false; } }
表示在select的结果集中,从0索引开始,选择一行数据。
测试代码通过。