本文介紹了MyBatis支持多個數(shù)據(jù)庫的處理方法,對大家解決問題具有一定的參考價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧!
問題描述
我有不同的客戶使用不同的數(shù)據(jù)庫供應(yīng)商(Postgres、Oracle、MySQL等)
我希望只編寫一次代碼,并且能夠在不同的數(shù)據(jù)庫上運行。
實現(xiàn)這一目標(biāo)的”mybatis”方法是什么?
我到目前為止發(fā)現(xiàn)的問題,例如:
Postgres在CREATE SQL語句中有一個”如果不存在”的概念。Oracle不支持此功能。
Oracle在SQL語法中不支持”Limit”和”Offset”,而其他數(shù)據(jù)庫則支持。
DDL語句中的文本(Postgres)與lob(Oracle和其他)JDBC類型。
我不想重復(fù)我的查詢(這就是我到目前為止所做的)。也許有一種更優(yōu)雅的方式可以做到這一點。
我正在使用mybatis Java批注。
推薦答案
您說您使用的是批注,但我建議您使用XML語言。我通常覺得它更清楚,特別是因為查詢中的某些部分無論如何都需要完全更改。
下面是一個DDL示例,其中為相同的方法獲取了兩個不同的XML元素,但數(shù)據(jù)庫ID不同。這些表非常相似,但由于您檢查表是否已經(jīng)存在以及類型是否完全不同的方式,您無法真正避免為此使用不同的SQL代碼:
<update id="createTables" databaseId="postgresql">
DO $$
BEGIN
CREATE TABLE IF NOT EXISTS item (
id SERIAL PRIMARY KEY,
content TEXT,
creation_datetime TIMESTAMPTZ DEFAULT NOW(),
modification_datetime TIMESTAMPTZ
);
END$$
</update>
<update id="createTables" databaseId="sqlserver">
IF NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'item')
BEGIN
CREATE TABLE item (
id INT IDENTITY PRIMARY KEY,
content NVARCHAR(MAX),
creation_datetime DATETIMEOFFSET DEFAULT SYSDATETIMEOFFSET(),
modification_datetime DATETIMEOFFSET
);
END
</update>
您也可以將查詢放在一個SQL元素中,但根據(jù)數(shù)據(jù)庫ID使用<if>(或<choose>)更改不同的節(jié)。如果更改很小,則效果很好:
<delete id="deleteItem">
DELETE FROM my_item
WHERE gid=CAST(#{gid} AS <if test="_databaseId == 'postgresql'">UUID</if><if test="_databaseId == 'sqlserver'">UNIQUEIDENTIFIER</if>)
</delete>
<select id="getLatestSomething" resultType="test.Something">
SELECT <if test="_databaseId == 'sqlserver'">TOP 1</if> *
FROM something
ORDER BY creation_datetime DESC
<if test="_databaseId == 'postgresql'">
LIMIT 1
</if>
</select>
對于不同的數(shù)據(jù)庫ID使用不同的查詢元素,還是在相同的查詢元素中只使用條件片段,這是一個可讀性問題。它可能非常主觀,具體取決于查詢的復(fù)雜程度。
例如,我發(fā)現(xiàn)下面使用PostgreSQL和SQL Server的”upsert”很難讀懂。最好放在單獨的元素中:
<insert id="insertStuff" parameterType="somestuff.Stuff">
<if test="_databaseId == 'postgresql'">
INSERT INTO my_stuff (...)
</if>
<if test="_databaseId == 'sqlserver'">
MERGE INTO my_stuff WITH (HOLDLOCK) AS t USING (
</if>
VALUES (#{...},
<if test="_databaseId == 'postgresql'">
CAST(#{jsonData} AS JSONB)
</if>
<if test="_databaseId == 'sqlserver'">
#{jsonData}
</if>
)
<if test="_databaseId == 'postgresql'">
ON CONFLICT DO NOTHING
</if>
<if test="_databaseId == 'sqlserver'">
)
AS s (...)
ON t....=s....
AND t....=s....
WHEN NOT MATCHED BY TARGET THEN
INSERT (...)
VALUES (s...., s....);
</if>
</insert>
MyBatis Dynamic SQL documentation中有關(guān)于所有這些的更多信息。
假設(shè)您的XML映射器文件位于mypackage/MyMapper.xml中,則可以在與該目錄匹配的包中使用MyMapperJava接口。沒有特定于該數(shù)據(jù)庫ID的內(nèi)容。
package mypackage;
public interface MyMapper {
void createTables();
void deleteItem(@Param("gid") UUID gid);
Something getLatestSomething();
}
將MyBatis與Spring配合使用時,可以這樣設(shè)置供應(yīng)商配置:
@Bean
public VendorDatabaseIdProvider vendorDatabaseIdProvider() {
Properties vendorProperties = new Properties();
vendorProperties.setProperty("PostgreSQL", "postgresql");
vendorProperties.setProperty("SQL Server", "sqlserver");
// Add others as required, this will look for the substring in the product name coming
// from the database metadata.
// ...
VendorDatabaseIdProvider dbIdProvider = new VendorDatabaseIdProvider();
dbIdProvider.setProperties(vendorProperties);
return dbIdProvider;
}
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource, ApplicationContext appContext,
VendorDatabaseIdProvider vendorDatabaseIdProvider) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setDatabaseIdProvider(vendorDatabaseIdProvider);
SqlSessionFactory factory = bean.getObject();
return factory;
}
如果您沒有使用Spring,您應(yīng)該能夠使用XML configuration配置DatabaseIdProvider。
您并不嚴(yán)格需要數(shù)據(jù)庫ID提供程序。它所做的就是根據(jù)從DataSource獲得的產(chǎn)品名稱在配置中設(shè)置數(shù)據(jù)庫ID。大致是這樣的:
String databaseId = databaseIdProvider.getDatabaseId(dataSource);
configuration.setDatabaseId(databaseId);
(如果查看org.apache.ibatis.mapping.VendorDatabaseIdProvider的代碼,您會發(fā)現(xiàn)databaseIdProvider.getDatabaseId(...)只是在DatabaseMetaData.getDatabaseProductName()返回的內(nèi)容中查找匹配子字符串。如果需要,您也可以通過其他設(shè)置手動完成此操作。)
請注意,當(dāng)databaseId=""直接用作XML元素的屬性時,沒有下劃線,但當(dāng)用作測試條件時,稱為_databaseId。
這篇關(guān)于MyBatis支持多個數(shù)據(jù)庫的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,






