Skip to content

Commit

Permalink
Merge pull request #897 from jeffgbutler/member-of-implementation
Browse files Browse the repository at this point in the history
Add example of implementing the MySQL "member of" operator
  • Loading branch information
jeffgbutler authored Jan 15, 2025
2 parents 4db11ed + e31ca9b commit ac0a92c
Show file tree
Hide file tree
Showing 5 changed files with 227 additions and 2 deletions.
16 changes: 14 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -175,13 +175,13 @@
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<artifactId>junit-jupiter</artifactId>
<version>${test.containers.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<artifactId>postgresql</artifactId>
<version>${test.containers.version}</version>
<scope>test</scope>
</dependency>
Expand All @@ -203,6 +203,18 @@
<version>3.5.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>mysql</artifactId>
<version>${test.containers.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>9.1.0</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
1 change: 1 addition & 0 deletions src/test/java/config/TestContainersConfiguration.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@
public interface TestContainersConfiguration {
DockerImageName POSTGRES_LATEST = DockerImageName.parse("postgres:17.2");
DockerImageName MARIADB_LATEST = DockerImageName.parse("mariadb:11.6.2");
DockerImageName MYSQL_LATEST = DockerImageName.parse("mysql:9.1.0");
}
39 changes: 39 additions & 0 deletions src/test/java/examples/mysql/MemberOfCondition.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright 2016-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package examples.mysql;

import java.util.Objects;

import org.jspecify.annotations.NullMarked;
import org.mybatis.dynamic.sql.AbstractNoValueCondition;

@NullMarked
public class MemberOfCondition<T> extends AbstractNoValueCondition<T> {
private final String jsonArray;

protected MemberOfCondition(String jsonArray) {
this.jsonArray = Objects.requireNonNull(jsonArray);
}

@Override
public String operator() {
return "member of(" + jsonArray + ")";
}

public static <T> MemberOfCondition<T> memberOf(String jsonArray) {
return new MemberOfCondition<>(jsonArray);
}
}
51 changes: 51 additions & 0 deletions src/test/java/examples/mysql/MemberOfFunction.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright 2016-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package examples.mysql;

import java.util.Objects;

import org.jspecify.annotations.NullMarked;
import org.mybatis.dynamic.sql.BasicColumn;
import org.mybatis.dynamic.sql.BindableColumn;
import org.mybatis.dynamic.sql.render.RenderingContext;
import org.mybatis.dynamic.sql.select.function.AbstractTypeConvertingFunction;
import org.mybatis.dynamic.sql.util.FragmentAndParameters;

@NullMarked
public class MemberOfFunction<T> extends AbstractTypeConvertingFunction<T, Long, MemberOfFunction<T>> {

private final String jsonArray;

protected MemberOfFunction(BasicColumn column, String jsonArray) {
super(column);
this.jsonArray = Objects.requireNonNull(jsonArray);
}

@Override
protected MemberOfFunction<T> copy() {
return new MemberOfFunction<>(column, jsonArray);
}

@Override
public FragmentAndParameters render(RenderingContext renderingContext) {
return column.render(renderingContext)
.mapFragment(f -> f + " member of(" + jsonArray + ")");
}

public static <T> MemberOfFunction<T> memberOf(BindableColumn<T> column, String jsonArray) {
return new MemberOfFunction<>(column, jsonArray);
}
}
122 changes: 122 additions & 0 deletions src/test/java/examples/mysql/MySQLTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
* Copyright 2016-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package examples.mysql;

import static examples.mysql.MemberOfCondition.memberOf;
import static examples.mysql.MemberOfFunction.memberOf;
import static examples.mariadb.ItemsDynamicSQLSupport.*;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
import static org.mybatis.dynamic.sql.SqlBuilder.*;

import java.util.List;
import java.util.Map;

import config.TestContainersConfiguration;
import org.apache.ibatis.datasource.unpooled.UnpooledDataSource;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.mybatis.dynamic.sql.render.RenderingStrategies;
import org.mybatis.dynamic.sql.select.render.SelectStatementProvider;
import org.mybatis.dynamic.sql.util.mybatis3.CommonSelectMapper;
import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

@Testcontainers
class MySQLTest {

@SuppressWarnings("resource")
@Container
private static final MySQLContainer<?> mysql =
new MySQLContainer<>(TestContainersConfiguration.MYSQL_LATEST)
.withInitScript("examples/mariadb/CreateDB.sql");

private static SqlSessionFactory sqlSessionFactory;

@BeforeAll
static void setup() {
UnpooledDataSource ds = new UnpooledDataSource(mysql.getDriverClassName(), mysql.getJdbcUrl(),
mysql.getUsername(), mysql.getPassword());
Environment environment = new Environment("test", new JdbcTransactionFactory(), ds);
Configuration config = new Configuration(environment);
config.addMapper(CommonSelectMapper.class);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
}

@Test
void smokeTest() {
try (SqlSession session = sqlSessionFactory.openSession()) {
CommonSelectMapper mapper = session.getMapper(CommonSelectMapper.class);

SelectStatementProvider selectStatement = select(id, description)
.from(items)
.orderBy(id)
.build()
.render(RenderingStrategies.MYBATIS3);
List<Map<String, Object>> rows = mapper.selectManyMappedRows(selectStatement);
assertThat(rows).hasSize(20);
}
}

@Test
void testMemberOfAsCondition() {
try (SqlSession session = sqlSessionFactory.openSession()) {
CommonSelectMapper mapper = session.getMapper(CommonSelectMapper.class);

SelectStatementProvider selectStatement = select(id, memberOf(id, "'[1, 2, 3]'").as("inList"))
.from(items)
.where(id, memberOf("'[1, 2, 3]'"))
.orderBy(id)
.build()
.render(RenderingStrategies.MYBATIS3);

assertThat(selectStatement.getSelectStatement())
.isEqualTo("select id, id member of('[1, 2, 3]') as inList from items where id member of('[1, 2, 3]') order by id");

List<Map<String, Object>> rows = mapper.selectManyMappedRows(selectStatement);
assertThat(rows).hasSize(3);
assertThat(rows.get(2)).containsOnly(entry("id", 3), entry("inList", 1L));
}
}

@Test
void testMemberOfAsFunction() {
try (SqlSession session = sqlSessionFactory.openSession()) {
CommonSelectMapper mapper = session.getMapper(CommonSelectMapper.class);

SelectStatementProvider selectStatement = select(id, memberOf(id, "'[1, 2, 3]'").as("inList"))
.from(items)
.where(memberOf(id,"'[1, 2, 3]'"), isEqualTo(1L))
.orderBy(id)
.build()
.render(RenderingStrategies.MYBATIS3);

assertThat(selectStatement.getSelectStatement())
.isEqualTo("select id, id member of('[1, 2, 3]') as inList from items where id member of('[1, 2, 3]') = #{parameters.p1} order by id");

List<Map<String, Object>> rows = mapper.selectManyMappedRows(selectStatement);
assertThat(rows).hasSize(3);
assertThat(rows.get(2)).containsOnly(entry("id", 3), entry("inList", 1L));
}
}
}

0 comments on commit ac0a92c

Please sign in to comment.