12

我的项目同时使用Mybatis(用于将 java 持久化到数据库)和Mybatis Generator(从数据库模式自动生成映射器 xml 文件和 java 接口)。

Mybatis 生成器在生成基本 crud 操作所需的文件方面做得很好。

语境

对于某些表/类,我们将需要比 MyBatis Generator 工具生成的“粗略的东西”更多的“东西”(代码查询等)。

有没有办法拥有“两全其美”,即使用自动生成以及“自定义代码”。您如何分离和构造“手动编辑的文件”和“自动生成的文件”。

提议

我正在考虑以下内容,即表“Foo”

自动生成

  • FooCrudMapper.xml
  • 接口 FooCrud.java

(其中“Crud”代表“创建读取更新删除”)

手工编辑

  • FooMapper.xml
  • 接口 Foo 扩展了 FooCrud

概念:如果架构发生更改,您始终可以安全地自动生成“Crud”xml 和 .java 文件,而无需清除任何自定义更改。

问题

  • mybatis 会正确处理这种情况,即这个映射器会正确执行自动生成的“粗略代码”吗?

    FooMapper fooMapper = sqlSession.getMapper(FooMapper.class);

  • 你推荐什么方法?

编辑1:*我们的数据库设计使用“核心表”(“元素”)和其他表“扩展”该表并添加额外属性(共享键)。我查看了文档和消息来源得出结论,如果没有任何手动编辑,我不能将 Mybatis Generator 与这种“扩展”结合使用:

即这不起作用。-ElementMapper 扩展“ElementCrudMapper” -FooMapper.xml 扩展“ElementCrudMapper”和“FooCrudMapper”

谢谢大家!

4

4 回答 4

20

我可以分离出生成的文件和手工编辑的文件。

我使用 mybatis-spring 和 spring 来管理 dao 接口。这个库允许 MyBatis 参与 Spring 事务,负责构建 MyBatis 映射器和 SqlSessions 并将它们注入其他 bean,将 MyBatis 异常转换为 Spring DataAccessExceptions,最后,它允许您构建您的应用程序代码,而不依赖于 MyBatis、Spring 或MyBatis-Spring .

对于 DAO Interfaces,我编写了一个通用的 MybatisBaseDao 来表示由 mybatis 生成器生成的基本接口。

    public interface MybatisBaseDao<T, PK extends Serializable, E> {
    int countByExample(E example);

    int deleteByExample(E example);

    int deleteByPrimaryKey(PK id);

    int insert(T record);

    int insertSelective(T record);

    List<T> selectByExample(E example);

    T selectByPrimaryKey(PK id);

    int updateByExampleSelective(@Param("record") T record, @Param("example") E example);

    int updateByExample(@Param("record") T record, @Param("example") E example);

    int updateByPrimaryKeySelective(T record);

    int updateByPrimaryKey(T record);
    }

当然,您可以BaseDao根据自己的需求定制。例如我们有一个UserDao,那么你可以这样定义它

public interface UserDao extends MybatisBaseDao<User, Integer, UserExample>{
    List<User> selectUserByAddress(String address); // hand edited query method
}

对于 mapper xml 文件,我在 mapper(.xml) 基本文件夹中创建了两个包来分隔生成的文件和手工编辑的文件。对于UserDao上面,我将生成器生成的 UserMapper.xml 放在名为“生成”的包中。我将所有手写映射器 sql 放入名为 .xml 的包中的另一个 UserMapper.xml 文件中manual。这两个映射器文件以相同的头文件开头<mapper namespace="com.xxx.dao.UserDao" >。Mybatis 可以扫描 xml mapper 文件自动映射 sql 和对应的接口方法。

对于生成的实体和示例对象,我直接覆盖它们。

希望上面的方法可以帮到你!

于 2013-11-08T04:53:23.080 回答
2

Larry.Z 解决方案帮助我解决了将自动生成的文件与手动编辑的文件分开的相同问题。我在我的项目中有一个自定义文件夹结构,并调整了 Larry 解决方案以在我的项目中工作,并通过使用 Larry 解决方案调整它来添加这个答案以帮助其他人。

最好的解决方案是在 Mybatis 生成器 ( MBG ) 中添加功能以集成手动修改的 xml 映射器。MBG 必须添加解析功能以将相应的手节点方法添加到客户端映射器界面,但现在此功能不存在,因此我使用并调整了 Larry.Z 解决方案。

在我的项目中,我使用:

<properties>  
...  
<java.version>1.7</java.version>  
<spring.version>3.2.2.RELEASE</spring.version>  
<mybatis.version>3.2.2</mybatis.version>  
<mybatis-spring.version>1.2.0</mybatis-spring.version>  
<mybatis-generator-core.version>1.3.2</mybatis-generator-core.version>  
...  
</properties>  

我的文件夹结构是:

<base>/dao/: MBG 生成的 dao 类

<base>/dao/extended/: 扩展生成类 ( <DaoGeneratedName>Extended)

<base>/sqlmap/: MBG 生成的客户端接口和对应的 xml 映射器

<base>/sqlmap/extended/
hand xml mapper 和hand client Interface
( <InterfaceGenerated>Extended extends InterfaceGenerated {...)

<base>/sqlmap/generated/: MBG 生成的映射器命名空间的副本已更改

我已经配置好Mybatis-spring

<bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer"  
        p:basePackage="<base>.sqlmap"  
        p:sqlSessionTemplate-ref="sqlSessionTemplate"  
        p:nameGenerator-ref="myBeanNameGenerator"  
    />  

仅当您需要像我这样的自定义名称时才实施 myBeanNameGenerator。在此示例中,您可以删除行p:nameGenerator-ref="myBeanNameGenerator"

如果您的所有客户端接口都被扩展,您可以在上面替换
p:basePackage="<base>.sqlmap.extended"

(我的项目配置很大,所以我提取了最重要的一点)

这是我的客户端界面和映射器手动编码的示例:

import <base>.dao.Countries;
import <base>.sqlmap.CountriesMapper;
import org.apache.ibatis.annotations.Param;

public interface CountriesMapperExtended extends CountriesMapper {

/**
 *
 * @param code
 * @return
 */
Countries selectByCountryCode(@Param("code") String code);

}

CountryMapper 在哪里是 MBG 生成的客户端接口

手工编码的相关 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="<base>.sqlmap.extended.CountriesMapperExtended">

  <select id="selectByCountryCode" parameterType="java.lang.String" resultMap="BaseResultMap">
    select 
    <include refid="Base_Column_List" />
    from countries co
    where co.countrycode = #{code,jdbcType=VARCHAR}
  </select>

</mapper>

为了完成所有工作,我必须将 MBG 生成的所有接口方法集成到 xml 映射器中,为此,我复制了 MBG 生成的 xml 映射器<base>/sqlmap/generated/并更改了他的命名空间:

<?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="<base>.sqlmap.extended.CountriesMapperExtended">
... unchanged ...
</mapper>  

当数据库更改时会出现问题,我必须使用 MBG 来反映新的数据库结构。

所以我快速创建了一个 bash 脚本来监视<base>/sqlmap/extended/并检查是否有手动编码的 xml 映射器。如果有手工编码的 xml 映射器,复制相应的 MBG 生成更改他的命名空间。

所有这些代码都不是一个优雅的解决方案,但可以工作。

所以bash脚本会覆盖文件<base>/sqlmap/generated/,而不是把你的代码放到这个文件夹中。

制作项目的备份副本并修改 bash 脚本,以自定义它并由您负责使用。

#!/bin/bash
CURDIR="$(pwd)"
SCRIPT_DIR=`dirname $0`
usage()
{
cat << EOF
usage: $0 options

This script is usefull to generate xml map to extend mybatis 
generator client interfaces. It suppose this structure:
<base>/sqlmap/           : generated xml mapper and interfaces
<base>/sqlmap/extended/  : extended xml mapper and interfaces
<base>/sqlmap/generated/ : copy of generated xml mapper changing
                           its namespace

If exist a mapper xml in <base>/sqlmap/extend identify by a name 
ending in Extended this script generate a copy of original generated
xml map of extended interface changing then namespace to reflect the 
extended Interface in <base>/sqlmap/generated.

This script require a list of base path:
$0 path1 path2 ...

Required parameters are marked by an *

OPTIONS:
  -h, --help          Show this message

EOF
}

declare -a BASES
let INDEX=0
TEMP=`getopt -o "hb:" --long "help,base:" -n "$0" -- "$@"`
eval set -- "$TEMP"
while true ; do
    case "$1" in
        -h|--help) 
            usage
            exit 1 ;;
        --) 
            shift ;
            break ;;
        *) 
            echo "Too mutch parametes!!! abort." ;
            exit 1 ;;
    esac
done
#process all paths
let INDEX=0
BASE="$1"
while [ "${BASE:0:1}" == "/" ]
do
    shift ;
    BASES[$INDEX]="$BASE"
    let INDEX+=1
    BASE="$1"
done
if [ "$INDEX" -le "0" ]
then
    echo "--bases options cannot be emplty"
    usage
    exit 1
fi

for BASE in ${BASES[@]}
do
    if [ ! -d "$BASE" ]
    then
        echo "Error: every base parameter must be a folder!!"
        echo "Base=$BASE is not a folder"
        usage
        exit 1
    fi
    SQLMAP="$BASE/sqlmap"
    if [ ! -d "$SQLMAP" ]
    then
        echo "Error: every base parameter must have a sqlmap folder!!"
        echo "$SQLMAP is not a folder"
        usage
        exit 1
    fi
    EXTENDED="$BASE/sqlmap/extended"
    if [ ! -d "$EXTENDED" ]
    then
        echo "Error: every base parameter must have a sqlmap/extended folder!!"
        echo "$EXTENDED is not a folder"
        usage
        exit 1
    fi
    GENERATED="$BASE/sqlmap/generated"
    if [ ! -d "$GENERATED" ]
    then
        mkdir -p "$GENERATED"
    fi
    while IFS= read -r -d '' file
    do
        name="${file##*/}" 
        #path="${file%/*}"
        ext=".${name##*.}"
        nameNoSuffix="${name%$ext}"
        nameBase="${nameNoSuffix%Extended}"
        sed -r 's/<mapper namespace="(.+)\.([^."]+)"\s*>\s*$/<mapper namespace="\1.extended.\2Extended">/' "$SQLMAP/$nameBase.xml" > "$GENERATED/$nameNoSuffix.xml"
    done < <(eval "find $EXTENDED/ -type f -name \*Extended\.xml -print0")

done
exit 0

脚本的使用

$ ./post-generator.sh "/home/...<base>"不要把最后一个/放在路径上

这个路径是包含sqlmap、sqlmap/extended、sqlmap/generated的文件夹的路径

如果您像我一样拥有多个路径,则可以使用路径列表

为了通过 maven 使用它,我在项目 pom.xml 中使用了这个插件:

            <plugin>
                <artifactId>exec-maven-plugin</artifactId>
                <groupId>org.codehaus.mojo</groupId>
                <version>1.2.1</version>
                <executions>
                    <execution>
                        <id>build client extended xml</id>
                        <goals>
                            <goal>exec</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <executable>${basedir}/scripts/post-generator.sh</executable>
                    <workingDirectory>${basedir}/scripts</workingDirectory>
                    <arguments>
                        <argument>${basedir}/<basepath1></argument>
                        <argument>${basedir}/<basepath2></argument>
                    </arguments>
                </configuration>
            </plugin>

在项目文件夹中,您可以使用$ mvn exec:exec$ mvn mybatis-generator:generate exec:exec

如果您使用 Netbeans,您可以配置项目操作以在mybatis-generator:generate exec:exec不离开 Netbeans 的情况下运行这些目标。当数据库结构发生变化时,您可以手动启动它。

现在您可以毫无问题地使用扩展映射器,如果数据库结构发生变化,让 MBG 完成他的工作。

在您的 bean 中,您可以注入具有
自动生成的 MBG 方法以及您的手动编码方法的扩展接口:

    <bean id="service" class="<base>.services.ServiceImpl" scope="singleton" 
        ...
        p:countriesMapper-ref="countriesMapperExtended"
        ...
        p:sqlSessionTemplate-ref="sqlSessionTemplate"
    />

countryMapperExtended bean 由上面的 mapperScanner 生成。

于 2014-04-15T11:51:37.283 回答
0

我已经给出了一个可行的答案,但由于配置庞大,它很复杂且不容易理解。

现在我找到了一个更好,更简洁,更容易的答案。
我受到 Emacarron 帖子的启发:Fix #35

我使用了 mbg,并在 generatorConfig.xml 中使用了<javaClientGenerator type="XMLMAPPER" ...>在文件夹映射器上生成 java 接口和 xml 映射配置。

所以在我的映射器文件夹示例中,我有:

  • AnagraficaMapper.java
  • AnafigraficaMapper.xml

在模型文件夹中我有

  • Anagrafica.java
  • AnagraficaKey.java
  • AnagraficaExample.java

前两个是对象模型,扩展它们是微不足道的。
为了扩展映射器,我只需复制并清空
AnagraficaMapper.java --> AnagraficaExMapper.java
AnagraficaMapper.xml --> AnagraficaExMapper.xml
在这两个新文件中我放置了我的新代码。
举个例子我决定添加一个新的sql selectByPrimaryKeyMy

public interface AnagraficaExMapper extends AnagraficaMapper {
    Anagrafica selectByPrimaryKeyMy(AnagraficaKey key);
}  

这是我扩展 mgb 生成的 AnagraficaMapper 接口的接口。
在 AnagraficaExMapper.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="net.algoritmica.ciaomondo.mapper.AnagraficaExMapper" >
  <select id="selectByPrimaryKeyMy" parameterType="net.algoritmica.ciaomondo.model.AnagraficaKey" resultMap="net.algoritmica.ciaomondo.mapper.AnagraficaMapper.BaseResultMap">
    select 
      <include refid="net.algoritmica.ciaomondo.mapper.AnagraficaMapper.Base_Column_List" />
    from ANAGRAFICA anag
    where anag.IDANAGRAFICA = #{idanagrafica,jdbcType=INTEGER}
  </select>
</mapper>  

你怎么能看到命名空间是...AnagraficaExMapper 指向新的扩展接口。

通过Fix#35的解决方案,当MyBatis在AnagraficaExMapper.java中搜索代码并找到selectByPrimaryKeyMy方法时,这也是在AnagraficaExMapper.xml中建立的;

但是当搜索像 selectByPrimaryKey 这样的分层方法时,这在 AnagraficaExMapper.xml 中没有找到,但感谢Fix #35,代码也在父名称上搜索,绑定了旧的 AnagraficaMapper.xml 中的所有扩展接口方法

要包含旧 xml 文件中包含的片段,您必须使用旧 xml 文件的完整路径,例如:<include refid="net.algoritmica.ciaomondo.mapper.AnagraficaMapper.Base_Column_List" />

现在你可以简单地配置 MyBatis 来进行自动映射器扫描和正确绑定到 xml 映射器的所有接口。
当您使用 mbg 感觉 db 更改时,界面已重新生成,但新的扩展界面并未被覆盖,因此您的代码被保存。

问候

于 2017-03-07T16:37:45.207 回答
0

我在 Spring Boot 项目中有这个任务,并在下面得到解决

在此处输入图像描述

在 mybatis/*.xml 文件中,我将像这样生成的更改<mapper namespace="news.project.demo.mappers.BrandMapper"><mapper namespace="news.project.demo.mappers.extended.BrandMapperExtended"> 但所有 sql 逻辑都必须写在 xml 文件中。接口只是没有@Select 或@ResultMap 注解的声明,所以你必须正确配置你的mybatis-generator。

在 application.properties 我有

mybatis.mapper-locations=classpath*:mybatis/*.xml mybatis.type-aliases-package=news.project.demo.models

于 2018-09-14T08:58:30.567 回答