Skip to content

Commit ec3e259

Browse files
authored
Merge pull request #853 from kazuki43zoo/allow-default-value-on-placeholder
Allow to specify a default value on placeholder
2 parents 6537e21 + 4aaf6eb commit ec3e259

File tree

15 files changed

+944
-5
lines changed

15 files changed

+944
-5
lines changed

src/main/java/org/apache/ibatis/parsing/PropertyParser.java

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2009-2015 the original author or authors.
2+
* Copyright 2009-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -19,9 +19,33 @@
1919

2020
/**
2121
* @author Clinton Begin
22+
* @author Kazuki Shimizu
2223
*/
2324
public class PropertyParser {
2425

26+
private static final String KEY_PREFIX = "org.apache.ibatis.parsing.PropertyParser.";
27+
/**
28+
* The special property key that indicate whether enable a default value on placeholder.
29+
* <p>
30+
* The default value is {@code false} (indicate disable a default value on placeholder)
31+
* If you specify the {@code true}, you can specify key and default value on placeholder (e.g. {@code ${db.username:postgres}}).
32+
* </p>
33+
* @since 3.4.2
34+
*/
35+
public static final String KEY_ENABLE_DEFAULT_VALUE = KEY_PREFIX + "enable-default-value";
36+
37+
/**
38+
* The special property key that specify a separator for key and default value on placeholder.
39+
* <p>
40+
* The default separator is {@code ":"}.
41+
* </p>
42+
* @since 3.4.2
43+
*/
44+
public static final String KEY_DEFAULT_VALUE_SEPARATOR = KEY_PREFIX + "default-value-separator";
45+
46+
private static final String ENABLE_DEFAULT_VALUE = "false";
47+
private static final String DEFAULT_VALUE_SEPARATOR = ":";
48+
2549
private PropertyParser() {
2650
// Prevent Instantiation
2751
}
@@ -33,18 +57,41 @@ public static String parse(String string, Properties variables) {
3357
}
3458

3559
private static class VariableTokenHandler implements TokenHandler {
36-
private Properties variables;
60+
private final Properties variables;
61+
private final boolean enableDefaultValue;
62+
private final String defaultValueSeparator;
3763

38-
public VariableTokenHandler(Properties variables) {
64+
private VariableTokenHandler(Properties variables) {
3965
this.variables = variables;
66+
this.enableDefaultValue = Boolean.parseBoolean(getPropertyValue(KEY_ENABLE_DEFAULT_VALUE, ENABLE_DEFAULT_VALUE));
67+
this.defaultValueSeparator = getPropertyValue(KEY_DEFAULT_VALUE_SEPARATOR, DEFAULT_VALUE_SEPARATOR);
68+
}
69+
70+
private String getPropertyValue(String key, String defaultValue) {
71+
return (variables == null) ? defaultValue : variables.getProperty(key, defaultValue);
4072
}
4173

4274
@Override
4375
public String handleToken(String content) {
44-
if (variables != null && variables.containsKey(content)) {
45-
return variables.getProperty(content);
76+
if (variables != null) {
77+
String key = content;
78+
if (enableDefaultValue) {
79+
final int separatorIndex = content.indexOf(defaultValueSeparator);
80+
String defaultValue = null;
81+
if (separatorIndex >= 0) {
82+
key = content.substring(0, separatorIndex);
83+
defaultValue = content.substring(separatorIndex + defaultValueSeparator.length());
84+
}
85+
if (defaultValue != null) {
86+
return variables.getProperty(key, defaultValue);
87+
}
88+
}
89+
if (variables.containsKey(key)) {
90+
return variables.getProperty(key);
91+
}
4692
}
4793
return "${" + content + "}";
4894
}
4995
}
96+
5097
}

src/site/es/xdoc/configuration.xml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,44 @@ SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environ
9292
</ul>
9393
<p>Por tanto las properties más prioritarias son las pasadas como parámetro, seguidas de los atributos tipo classpath/url y finalmente las propiedades especificadas en el elemento properties..
9494
</p>
95+
96+
<p>
97+
Since the MyBatis 3.4.2, your can specify a default value into placeholder as follow:
98+
</p>
99+
<source><![CDATA[
100+
<dataSource type="POOLED">
101+
<!-- ... -->
102+
<property name="username" value="${username:ut_user}"/> <!-- If 'username' property not present, username become 'ut_user' -->
103+
</dataSource>]]></source>
104+
105+
<p>
106+
This feature is disabled by default. If you specify a default value into placeholder,
107+
you should be enable this feature by adding a special property as follow:
108+
</p>
109+
110+
<source><![CDATA[
111+
<properties resource="org/mybatis/example/config.properties">
112+
<!-- ... -->
113+
<property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/> <!-- Enable this feature -->
114+
</properties>]]></source>
115+
116+
<p>
117+
<span class="label important">NOTE</span> Also If you are used already the <code>":"</code> as property key(e.g. <code>db:username</code>)
118+
or you are used already the ternary operator of OGNL expression(e.g. <code>${tableName != null ? tableName : 'global_constants'}</code>) on your sql definition,
119+
you should be change the character that separate key and default value by adding a special property as follow:
120+
</p>
121+
122+
<source><![CDATA[
123+
<properties resource="org/mybatis/example/config.properties">
124+
<!-- ... -->
125+
<property name="org.apache.ibatis.parsing.PropertyParser.default-value-separator" value="?:"/> <!-- Change default value of separator -->
126+
</properties>]]></source>
127+
<source><![CDATA[
128+
<dataSource type="POOLED">
129+
<!-- ... -->
130+
<property name="username" value="${db:username?:ut_user}"/>
131+
</dataSource>]]></source>
132+
95133
</subsection>
96134
<subsection name="settings">
97135
<p>Son muy importantes para definir cómo se comporta MyBatis en ejecución. La siguiente tabla describe las configuraciones (settings), sus significados y sus valores por defecto.</p>

src/site/ja/xdoc/configuration.xml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,43 @@ SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environ
100100
<p>
101101
従って、メソッド引数として渡されたプロパティが最も優先度が高く、次に resource/url 属性、最も優先度が低いのは properties 要素のボディで指定された値ということになります。
102102
</p>
103+
104+
<p>
105+
MyBatis 3.4.2以降では、下記に示すようにプレースホルダの中にデフォルト値を指定することができます。
106+
</p>
107+
<source><![CDATA[
108+
<dataSource type="POOLED">
109+
<!-- ... -->
110+
<property name="username" value="${username:ut_user}"/> <!-- 'username'プロパティが存在しない場合は、 usernameは'ut_user'になる -->
111+
</dataSource>]]></source>
112+
113+
<p>
114+
この機能はデフォルトでは無効になっています。もしプレースホルダの中にデフォルト値を指定したい場合は、下記に示すように特別なプロパティを追加して機能を有効化する必要があります。
115+
</p>
116+
117+
<source><![CDATA[
118+
<properties resource="org/mybatis/example/config.properties">
119+
<!-- ... -->
120+
<property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/> <!-- この機能を有効化 -->
121+
</properties>]]></source>
122+
123+
<p>
124+
<span class="label important">NOTE</span> また、既にプロパティキーとして<code>":"</code>を使用(例: <code>db:username</code>)していたり、
125+
SQL定義の中でOGNL式の三項演算子(例: <code>${tableName != null ? tableName : 'global_constants'}</code>)を使用している場合は、
126+
下記に示すように特別なプロパティを追加してキーとデフォルト値を分割するための文字を変更する必要があります。
127+
</p>
128+
129+
<source><![CDATA[
130+
<properties resource="org/mybatis/example/config.properties">
131+
<!-- ... -->
132+
<property name="org.apache.ibatis.parsing.PropertyParser.default-value-separator" value="?:"/> <!-- 分割文字を変更 -->
133+
</properties>]]></source>
134+
<source><![CDATA[
135+
<dataSource type="POOLED">
136+
<!-- ... -->
137+
<property name="username" value="${db:username?:ut_user}"/>
138+
</dataSource>]]></source>
139+
103140
</subsection>
104141
<subsection name="settings">
105142
<p>

src/site/ko/xdoc/configuration.xml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,44 @@ SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environ
8383
<li>마지막으로 메소드 파라미터로 전달된 속성을 읽는다. 앞서 로드된 값을 덮어쓴다.</li>
8484
</ul>
8585
<p>그래서 가장 우선순위가 높은 속성은 메소드의 파라미터로 전달된 값이고 그 다음은 자원및 url 속성이고 마지막은 properties 엘리먼트에 명시된 값이다.</p>
86+
87+
<p>
88+
Since the MyBatis 3.4.2, your can specify a default value into placeholder as follow:
89+
</p>
90+
<source><![CDATA[
91+
<dataSource type="POOLED">
92+
<!-- ... -->
93+
<property name="username" value="${username:ut_user}"/> <!-- If 'username' property not present, username become 'ut_user' -->
94+
</dataSource>]]></source>
95+
96+
<p>
97+
This feature is disabled by default. If you specify a default value into placeholder,
98+
you should be enable this feature by adding a special property as follow:
99+
</p>
100+
101+
<source><![CDATA[
102+
<properties resource="org/mybatis/example/config.properties">
103+
<!-- ... -->
104+
<property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/> <!-- Enable this feature -->
105+
</properties>]]></source>
106+
107+
<p>
108+
<span class="label important">NOTE</span> Also If you are used already the <code>":"</code> as property key(e.g. <code>db:username</code>)
109+
or you are used already the ternary operator of OGNL expression(e.g. <code>${tableName != null ? tableName : 'global_constants'}</code>) on your sql definition,
110+
you should be change the character that separate key and default value by adding a special property as follow:
111+
</p>
112+
113+
<source><![CDATA[
114+
<properties resource="org/mybatis/example/config.properties">
115+
<!-- ... -->
116+
<property name="org.apache.ibatis.parsing.PropertyParser.default-value-separator" value="?:"/> <!-- Change default value of separator -->
117+
</properties>]]></source>
118+
<source><![CDATA[
119+
<dataSource type="POOLED">
120+
<!-- ... -->
121+
<property name="username" value="${db:username?:ut_user}"/>
122+
</dataSource>]]></source>
123+
86124
</subsection>
87125
<subsection name="settings">
88126
<p>런타임시 마이바티스의 행위를 조정하기 위한 중요한 값들이다. 다음표는 설정과 그 의미 그리고 디폴트 값을 설명한다.</p>

src/site/xdoc/configuration.xml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,44 @@ SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environ
127127
the
128128
properties specified in the body of the properties element.
129129
</p>
130+
131+
<p>
132+
Since the MyBatis 3.4.2, your can specify a default value into placeholder as follow:
133+
</p>
134+
<source><![CDATA[
135+
<dataSource type="POOLED">
136+
<!-- ... -->
137+
<property name="username" value="${username:ut_user}"/> <!-- If 'username' property not present, username become 'ut_user' -->
138+
</dataSource>]]></source>
139+
140+
<p>
141+
This feature is disabled by default. If you specify a default value into placeholder,
142+
you should be enable this feature by adding a special property as follow:
143+
</p>
144+
145+
<source><![CDATA[
146+
<properties resource="org/mybatis/example/config.properties">
147+
<!-- ... -->
148+
<property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/> <!-- Enable this feature -->
149+
</properties>]]></source>
150+
151+
<p>
152+
<span class="label important">NOTE</span> Also If you are used already the <code>":"</code> as property key(e.g. <code>db:username</code>)
153+
or you are used already the ternary operator of OGNL expression(e.g. <code>${tableName != null ? tableName : 'global_constants'}</code>) on your sql definition,
154+
you should be change the character that separate key and default value by adding a special property as follow:
155+
</p>
156+
157+
<source><![CDATA[
158+
<properties resource="org/mybatis/example/config.properties">
159+
<!-- ... -->
160+
<property name="org.apache.ibatis.parsing.PropertyParser.default-value-separator" value="?:"/> <!-- Change default value of separator -->
161+
</properties>]]></source>
162+
<source><![CDATA[
163+
<dataSource type="POOLED">
164+
<!-- ... -->
165+
<property name="username" value="${db:username?:ut_user}"/>
166+
</dataSource>]]></source>
167+
130168
</subsection>
131169
<subsection name="settings">
132170
<p>

src/site/zh/xdoc/configuration.xml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,44 @@ SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environ
8989
</li>
9090
</ul>
9191
<p>因此,通过方法参数传递的属性具有最高优先级,resource/url 属性中指定的配置文件次之,最低优先级的是 properties 属性中指定的属性。</p>
92+
93+
<p>
94+
Since the MyBatis 3.4.2, your can specify a default value into placeholder as follow:
95+
</p>
96+
<source><![CDATA[
97+
<dataSource type="POOLED">
98+
<!-- ... -->
99+
<property name="username" value="${username:ut_user}"/> <!-- If 'username' property not present, username become 'ut_user' -->
100+
</dataSource>]]></source>
101+
102+
<p>
103+
This feature is disabled by default. If you specify a default value into placeholder,
104+
you should be enable this feature by adding a special property as follow:
105+
</p>
106+
107+
<source><![CDATA[
108+
<properties resource="org/mybatis/example/config.properties">
109+
<!-- ... -->
110+
<property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/> <!-- Enable this feature -->
111+
</properties>]]></source>
112+
113+
<p>
114+
<span class="label important">NOTE</span> Also If you are used already the <code>":"</code> as property key(e.g. <code>db:username</code>)
115+
or you are used already the ternary operator of OGNL expression(e.g. <code>${tableName != null ? tableName : 'global_constants'}</code>) on your sql definition,
116+
you should be change the character that separate key and default value by adding a special property as follow:
117+
</p>
118+
119+
<source><![CDATA[
120+
<properties resource="org/mybatis/example/config.properties">
121+
<!-- ... -->
122+
<property name="org.apache.ibatis.parsing.PropertyParser.default-value-separator" value="?:"/> <!-- Change default value of separator -->
123+
</properties>]]></source>
124+
<source><![CDATA[
125+
<dataSource type="POOLED">
126+
<!-- ... -->
127+
<property name="username" value="${db:username?:ut_user}"/>
128+
</dataSource>]]></source>
129+
92130
</subsection>
93131
<subsection name="settings" id="settings">
94132
<p>这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。下表描述了设置中各项的意图、默认值等。</p>
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/**
2+
* Copyright 2009-2016 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.apache.ibatis.parsing;
17+
18+
import org.hamcrest.core.Is;
19+
import org.junit.Assert;
20+
import org.junit.Test;
21+
22+
import java.util.Properties;
23+
24+
public class PropertyParserTest {
25+
26+
@Test
27+
public void replaceToVariableValue() {
28+
Properties props = new Properties();
29+
props.setProperty(PropertyParser.KEY_ENABLE_DEFAULT_VALUE, "true");
30+
props.setProperty("key", "value");
31+
props.setProperty("tableName", "members");
32+
props.setProperty("orderColumn", "member_id");
33+
props.setProperty("a:b", "c");
34+
Assert.assertThat(PropertyParser.parse("${key}", props), Is.is("value"));
35+
Assert.assertThat(PropertyParser.parse("${key:aaaa}", props), Is.is("value"));
36+
Assert.assertThat(PropertyParser.parse("SELECT * FROM ${tableName:users} ORDER BY ${orderColumn:id}", props), Is.is("SELECT * FROM members ORDER BY member_id"));
37+
38+
props.setProperty(PropertyParser.KEY_ENABLE_DEFAULT_VALUE, "false");
39+
Assert.assertThat(PropertyParser.parse("${a:b}", props), Is.is("c"));
40+
41+
props.remove(PropertyParser.KEY_ENABLE_DEFAULT_VALUE);
42+
Assert.assertThat(PropertyParser.parse("${a:b}", props), Is.is("c"));
43+
44+
}
45+
46+
@Test
47+
public void notReplace() {
48+
Properties props = new Properties();
49+
props.setProperty(PropertyParser.KEY_ENABLE_DEFAULT_VALUE, "true");
50+
Assert.assertThat(PropertyParser.parse("${key}", props), Is.is("${key}"));
51+
Assert.assertThat(PropertyParser.parse("${key}", null), Is.is("${key}"));
52+
53+
props.setProperty(PropertyParser.KEY_ENABLE_DEFAULT_VALUE, "false");
54+
Assert.assertThat(PropertyParser.parse("${a:b}", props), Is.is("${a:b}"));
55+
56+
props.remove(PropertyParser.KEY_ENABLE_DEFAULT_VALUE);
57+
Assert.assertThat(PropertyParser.parse("${a:b}", props), Is.is("${a:b}"));
58+
59+
}
60+
61+
@Test
62+
public void applyDefaultValue() {
63+
Properties props = new Properties();
64+
props.setProperty(PropertyParser.KEY_ENABLE_DEFAULT_VALUE, "true");
65+
Assert.assertThat(PropertyParser.parse("${key:default}", props), Is.is("default"));
66+
Assert.assertThat(PropertyParser.parse("SELECT * FROM ${tableName:users} ORDER BY ${orderColumn:id}", props), Is.is("SELECT * FROM users ORDER BY id"));
67+
Assert.assertThat(PropertyParser.parse("${key:}", props), Is.is(""));
68+
Assert.assertThat(PropertyParser.parse("${key: }", props), Is.is(" "));
69+
Assert.assertThat(PropertyParser.parse("${key::}", props), Is.is(":"));
70+
}
71+
72+
@Test
73+
public void applyCustomSeparator() {
74+
Properties props = new Properties();
75+
props.setProperty(PropertyParser.KEY_ENABLE_DEFAULT_VALUE, "true");
76+
props.setProperty(PropertyParser.KEY_DEFAULT_VALUE_SEPARATOR, "?:");
77+
Assert.assertThat(PropertyParser.parse("${key?:default}", props), Is.is("default"));
78+
Assert.assertThat(PropertyParser.parse("SELECT * FROM ${schema?:prod}.${tableName == null ? 'users' : tableName} ORDER BY ${orderColumn}", props), Is.is("SELECT * FROM prod.${tableName == null ? 'users' : tableName} ORDER BY ${orderColumn}"));
79+
Assert.assertThat(PropertyParser.parse("${key?:}", props), Is.is(""));
80+
Assert.assertThat(PropertyParser.parse("${key?: }", props), Is.is(" "));
81+
Assert.assertThat(PropertyParser.parse("${key?::}", props), Is.is(":"));
82+
}
83+
84+
}

0 commit comments

Comments
 (0)