我正在使用 Mybatis (用于持久化java到数据库)和 Mybatis Generator 的项目(从数据库自动生成mapper xml文件和java接口)模式)。
Mybatis生成器可以很好地生成基本crud操作所需的文件。
上下文
对于某些表/类,我们需要比MyBatis Generator工具生成的“crud stuff”更多的“东西”(代码查询等)。
有没有办法拥有“两全其美”,即使用自动生成以及“自定义代码”。如何分离和构建“手动编辑的文件”和“自动生成的文件”。
提案
我正在考虑以下内容,即表格“Foo”
自动生成
(其中“Crud”代表“创建读取更新删除”)
手工编辑
概念:如果架构发生了变化,您可以随时安全地自动生成“Crud”xml和.java文件,而不会消除任何自定义更改。
问题
mybatis能否正确处理这种情况,即此映射器是否会正确执行自动生成的“crud code”?
FooMapper fooMapper = sqlSession.getMapper(FooMapper.class);
您推荐什么方法?
编辑1: *我们的数据库设计使用“核心表”(“元素”),其他表“扩展”该表并添加额外的属性(共享密钥)。我查看了文档和来源得出的结论是,我不能将Mybatis Generator与这种“扩展”结合使用而不需要任何手动编辑:
即。这不起作用。 -ElementMapper扩展“ElementCrudMapper” -FooMapper.xml扩展了“ElementCrudMapper”和“FooCrudMapper”
谢谢大家!
答案 0 :(得分:21)
我可以分离生成的文件和手工编辑的文件。
我使用mybatis-spring和spring来管理dao接口。这个库允许MyBatis参与Spring事务,负责构建MyBatis映射器和SqlSessions并将它们注入其他bean,将MyBatis异常转换为Spring DataAccessExceptions,最后,它允许您构建您的应用程序代码,不依赖于MyBatis,Spring或MyBatis-Spring
对于DAO接口,我编写了一个通用的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放在名为'generated'的包中。我把所有手写mapper sqls放到名为manual
的包中的另一个UserMapper.xml文件中。两个映射器文件以相同的标头<mapper namespace="com.xxx.dao.UserDao" >
开头。 Mybatis可以扫描xml映射文件,自动映射sql和相应的接口方法。
对于生成的实体和示例对象,我直接覆盖它们。
我希望上面的方法可以帮到你!
答案 1 :(得分: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/
:
手xml映射器和手客户端接口
(<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);
}
其中CountriesMapper是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>
为了完成所有工作,我必须在xml mapper中集成所有生成的接口方法MBG,为此,我在<base>/sqlmap/generated/
中复制了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">
... unchanged ...
</mapper>
当db更改时,问题就出现了,我必须使用MBG来反映新的db结构。
所以我快速创建了一个在<base>/sqlmap/extended/
中观看的bash脚本,并检查是否有手动编码的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,则可以配置项目操作以在不离开Netbeans的情况下运行这些目标mybatis-generator:generate exec:exec
。当db结构发生变化时,您可以手动启动它。
现在你可以毫无问题地使用exended mapper,如果db结构发生变化,让MBG完成他的工作。
在你的bean中,你可以注入具有
的扩展接口
自动生成的MBG方法加上您的手工编码方法:
<bean id="service" class="<base>.services.ServiceImpl" scope="singleton"
...
p:countriesMapper-ref="countriesMapperExtended"
...
p:sqlSessionTemplate-ref="sqlSessionTemplate"
/>
其中countriesMapperExtended bean由上面的mapperScanner生成。
答案 2 :(得分:0)
我已经给出了一个有效的答案,但由于配置庞大,它很复杂且不易理解。
现在我找到了一个更好,更简洁的答案 我的灵感来自Emacarron的帖子:Fix #35
我使用了mbg并在generatorConfig.xml中我将<javaClientGenerator type="XMLMAPPER" ...>
放在文件夹映射器上生成java接口和xml映射配置。
所以在我的mapper文件夹示例中我有:
在模型文件夹中我有
前两个是对象模型,扩展它们是微不足道的
要扩展映射器,我只需复制并清空
AnagraficaMapper.java - &gt; AnagraficaExMapper.java
AnagraficaMapper.xml - &gt; 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感觉数据库更改时,接口重新生成但新的扩展接口未被覆盖,因此您的代码将被保存。
问候
答案 3 :(得分: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