diff --git a/jakarta-jpa/pom.xml b/jakarta-jpa/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..24cd5c525773d0856b57c5d22d219913f3e7fc34
--- /dev/null
+++ b/jakarta-jpa/pom.xml
@@ -0,0 +1,63 @@
+
+
+
+
+
+ mybatis-parent
+ io.mybatis
+ ${revision}
+
+ 4.0.0
+
+ mybatis-jakarta-jpa
+
+
+
+ io.mybatis
+ mybatis-provider
+
+
+
+ jakarta.persistence
+ jakarta.persistence-api
+ 3.1.0
+ provided
+
+
+
+
+ org.hsqldb
+ hsqldb
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ junit
+ junit
+
+
+ javax.persistence
+ javax.persistence-api
+ 2.2
+ compile
+
+
+
diff --git a/jakarta-jpa/src/main/java/io/mybatis/provider/jpa/JakartaJpaEntityClassFinder.java b/jakarta-jpa/src/main/java/io/mybatis/provider/jpa/JakartaJpaEntityClassFinder.java
new file mode 100644
index 0000000000000000000000000000000000000000..459721b1be92a2cbceab62be992feffd0880590e
--- /dev/null
+++ b/jakarta-jpa/src/main/java/io/mybatis/provider/jpa/JakartaJpaEntityClassFinder.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2020-2022 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
+ *
+ * http://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 io.mybatis.provider.jpa;
+
+import io.mybatis.provider.defaults.GenericEntityClassFinder;
+import jakarta.persistence.Table;
+
+/**
+ * 支持识别带有 @javax.persistence.Table 的实体类或者不带任何注解的POJO
+ *
+ * @author liuzh
+ */
+public class JakartaJpaEntityClassFinder extends GenericEntityClassFinder {
+
+ @Override
+ public boolean isEntityClass(Class> clazz) {
+ //带注解或不是简单类型和枚举的都算实体
+ return clazz.isAnnotationPresent(Table.class) || (!clazz.isPrimitive() && !SimpleTypeUtil.isSimpleType(clazz) && !clazz.isEnum());
+ }
+
+ @Override
+ public int getOrder() {
+ return super.getOrder() + 100;
+ }
+}
diff --git a/jakarta-jpa/src/main/java/io/mybatis/provider/jpa/JakartaJpaEntityColumnFactory.java b/jakarta-jpa/src/main/java/io/mybatis/provider/jpa/JakartaJpaEntityColumnFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..affc75c212aa6e79be5403a9dc1ba98f3d2c43ad
--- /dev/null
+++ b/jakarta-jpa/src/main/java/io/mybatis/provider/jpa/JakartaJpaEntityColumnFactory.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2020-2022 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
+ *
+ * http://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 io.mybatis.provider.jpa;
+
+import io.mybatis.provider.*;
+import jakarta.persistence.Column;
+import jakarta.persistence.Id;
+import jakarta.persistence.OrderBy;
+import jakarta.persistence.Transient;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * 通过 SPI 工厂扩展 EntityColumn 和 EntityTable
+ *
+ * @author liuzh
+ */
+public class JakartaJpaEntityColumnFactory implements EntityColumnFactory {
+
+ @Override
+ public Optional> createEntityColumn(EntityTable entityTable, EntityField field, Chain chain) {
+ Optional> optionalEntityColumns = chain.createEntityColumn(entityTable, field);
+ if (field.isAnnotationPresent(Transient.class)) {
+ return Optional.empty();
+ } else if (!optionalEntityColumns.isPresent()) {
+ //没有 @Transient 注解的字段都认为是表字段,不自动排除字段,字段名默认驼峰转下划线
+ optionalEntityColumns = Optional.of(Arrays.asList(EntityColumn.of(field).column(Style.getDefaultStyle().columnName(entityTable, field))));
+ }
+ if (optionalEntityColumns.isPresent()) {
+ List entityColumns = optionalEntityColumns.get();
+ for (EntityColumn entityColumn : entityColumns) {
+ EntityField entityField = entityColumn.field();
+ //主键
+ if (!entityColumn.id()) {
+ entityColumn.id(entityField.isAnnotationPresent(Id.class));
+ }
+ //列名
+ if (field.isAnnotationPresent(Column.class)) {
+ Column column = field.getAnnotation(Column.class);
+ String columnName = column.name();
+ if (!columnName.isEmpty()) {
+ entityColumn.column(columnName);
+ }
+ entityColumn.insertable(column.insertable()).updatable(column.updatable());
+ if (column.scale() != 0) {
+ entityColumn.numericScale(String.valueOf(column.scale()));
+ }
+ }
+ //只能默认空 ASC,或者写 ASC 或 DESC,不能写多个列
+ if (field.isAnnotationPresent(OrderBy.class)) {
+ OrderBy orderBy = field.getAnnotation(OrderBy.class);
+ if (orderBy.value().isEmpty()) {
+ entityColumn.orderBy("ASC");
+ } else {
+ entityColumn.orderBy(orderBy.value());
+ }
+ }
+ }
+ }
+ return optionalEntityColumns;
+ }
+
+ @Override
+ public int getOrder() {
+ return EntityColumnFactory.super.getOrder() + 100;
+ }
+
+}
diff --git a/jakarta-jpa/src/main/java/io/mybatis/provider/jpa/JakartaJpaEntityTableFactory.java b/jakarta-jpa/src/main/java/io/mybatis/provider/jpa/JakartaJpaEntityTableFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..baf384afee0f78f1ee8c2d43d7d27f508200c774
--- /dev/null
+++ b/jakarta-jpa/src/main/java/io/mybatis/provider/jpa/JakartaJpaEntityTableFactory.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2020-2022 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
+ *
+ * http://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 io.mybatis.provider.jpa;
+
+import io.mybatis.provider.EntityTable;
+import io.mybatis.provider.EntityTableFactory;
+import io.mybatis.provider.Style;
+import io.mybatis.provider.util.Utils;
+
+import jakarta.persistence.Table;
+
+/**
+ * 通过 SPI 工厂扩展 EntityColumn 和 EntityTable
+ *
+ * @author liuzh
+ */
+public class JakartaJpaEntityTableFactory implements EntityTableFactory {
+
+ @Override
+ public EntityTable createEntityTable(Class> entityClass, Chain chain) {
+ EntityTable entityTable = chain.createEntityTable(entityClass);
+ if (entityTable == null) {
+ entityTable = EntityTable.of(entityClass);
+ }
+ if (entityClass.isAnnotationPresent(Table.class)) {
+ Table table = entityClass.getAnnotation(Table.class);
+ if (!table.name().isEmpty()) {
+ entityTable.table(table.name());
+ }
+ } else if (Utils.isEmpty(entityTable.table())) {
+ //没有设置表名时,默认类名转下划线
+ entityTable.table(Style.getDefaultStyle().tableName(entityClass));
+ }
+ return entityTable;
+ }
+
+ @Override
+ public int getOrder() {
+ return EntityTableFactory.super.getOrder() + 100;
+ }
+
+}
diff --git a/jakarta-jpa/src/main/java/io/mybatis/provider/jpa/SimpleTypeUtil.java b/jakarta-jpa/src/main/java/io/mybatis/provider/jpa/SimpleTypeUtil.java
new file mode 100644
index 0000000000000000000000000000000000000000..8722935d3be1b6d981d451b46a4618bf37cd9ec9
--- /dev/null
+++ b/jakarta-jpa/src/main/java/io/mybatis/provider/jpa/SimpleTypeUtil.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2020-2022 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
+ *
+ * http://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 io.mybatis.provider.jpa;
+
+import io.mybatis.provider.util.Utils;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.sql.Timestamp;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * 参考 org.apache.ibatis.type.SimpleTypeRegistry
+ *
+ * @author Clinton Begin
+ * @author liuzh
+ */
+public class SimpleTypeUtil {
+ public static final String[] JAVA8_DATE_TIME = {
+ "java.time.Instant",
+ "java.time.LocalDateTime",
+ "java.time.LocalDate",
+ "java.time.LocalTime",
+ "java.time.OffsetDateTime",
+ "java.time.OffsetTime",
+ "java.time.ZonedDateTime",
+ "java.time.Year",
+ "java.time.Month",
+ "java.time.YearMonth"
+ };
+ private static final Set> SIMPLE_TYPE_SET = new HashSet>();
+
+ /**
+ * 特别注意:由于基本类型有默认值,因此在实体类中不建议使用基本类型作为数据库字段类型
+ */
+ static {
+ SIMPLE_TYPE_SET.add(byte[].class);
+ SIMPLE_TYPE_SET.add(String.class);
+ SIMPLE_TYPE_SET.add(Byte.class);
+ SIMPLE_TYPE_SET.add(Short.class);
+ SIMPLE_TYPE_SET.add(Character.class);
+ SIMPLE_TYPE_SET.add(Integer.class);
+ SIMPLE_TYPE_SET.add(Long.class);
+ SIMPLE_TYPE_SET.add(Float.class);
+ SIMPLE_TYPE_SET.add(Double.class);
+ SIMPLE_TYPE_SET.add(Boolean.class);
+ SIMPLE_TYPE_SET.add(Date.class);
+ SIMPLE_TYPE_SET.add(Timestamp.class);
+ SIMPLE_TYPE_SET.add(Class.class);
+ SIMPLE_TYPE_SET.add(BigInteger.class);
+ SIMPLE_TYPE_SET.add(BigDecimal.class);
+ //反射方式设置 java8 中的日期类型
+ for (String time : JAVA8_DATE_TIME) {
+ registerSimpleTypeSilence(time);
+ }
+ }
+
+ /**
+ * 注册新的类型
+ *
+ * @param clazz
+ */
+ public static void registerSimpleType(Class> clazz) {
+ SIMPLE_TYPE_SET.add(clazz);
+ }
+
+ /**
+ * 注册 8 种基本类型
+ */
+ public static void registerPrimitiveTypes() {
+ registerSimpleType(boolean.class);
+ registerSimpleType(byte.class);
+ registerSimpleType(short.class);
+ registerSimpleType(int.class);
+ registerSimpleType(long.class);
+ registerSimpleType(char.class);
+ registerSimpleType(float.class);
+ registerSimpleType(double.class);
+ }
+
+ /**
+ * 注册新的类型
+ *
+ * @param classes
+ */
+ public static void registerSimpleType(String classes) {
+ if (Utils.isNotEmpty(classes)) {
+ String[] cls = classes.split(",");
+ for (String c : cls) {
+ try {
+ SIMPLE_TYPE_SET.add(Class.forName(c));
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException("注册类型出错:" + c, e);
+ }
+ }
+ }
+ }
+
+ /**
+ * 注册新的类型,不存在时不抛出异常
+ *
+ * @param clazz
+ */
+ private static void registerSimpleTypeSilence(String clazz) {
+ try {
+ SIMPLE_TYPE_SET.add(Class.forName(clazz));
+ } catch (ClassNotFoundException e) {
+ //ignore
+ }
+ }
+
+ /**
+ * Tells us if the class passed in is a known common type
+ *
+ * @param clazz The class to check
+ * @return True if the class is known
+ */
+ public static boolean isSimpleType(Class> clazz) {
+ return SIMPLE_TYPE_SET.contains(clazz);
+ }
+
+}
diff --git a/jakarta-jpa/src/main/resources/META-INF/services/io.mybatis.provider.EntityClassFinder b/jakarta-jpa/src/main/resources/META-INF/services/io.mybatis.provider.EntityClassFinder
new file mode 100644
index 0000000000000000000000000000000000000000..edbba54a9c2d378dac8dfe33b732e3d643991482
--- /dev/null
+++ b/jakarta-jpa/src/main/resources/META-INF/services/io.mybatis.provider.EntityClassFinder
@@ -0,0 +1,17 @@
+#
+# Copyright 2020-2022 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
+#
+# http://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.
+#
+
+io.mybatis.provider.jpa.JakartaJpaEntityClassFinder
diff --git a/jakarta-jpa/src/main/resources/META-INF/services/io.mybatis.provider.EntityColumnFactory b/jakarta-jpa/src/main/resources/META-INF/services/io.mybatis.provider.EntityColumnFactory
new file mode 100644
index 0000000000000000000000000000000000000000..0978ba7fa30ab58e2980f29d941f641deadaa843
--- /dev/null
+++ b/jakarta-jpa/src/main/resources/META-INF/services/io.mybatis.provider.EntityColumnFactory
@@ -0,0 +1,17 @@
+#
+# Copyright 2020-2022 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
+#
+# http://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.
+#
+
+io.mybatis.provider.jpa.JakartaJpaEntityColumnFactory
diff --git a/jakarta-jpa/src/main/resources/META-INF/services/io.mybatis.provider.EntityTableFactory b/jakarta-jpa/src/main/resources/META-INF/services/io.mybatis.provider.EntityTableFactory
new file mode 100644
index 0000000000000000000000000000000000000000..3d556561f1b07397d3b707ae329a747da813ea87
--- /dev/null
+++ b/jakarta-jpa/src/main/resources/META-INF/services/io.mybatis.provider.EntityTableFactory
@@ -0,0 +1,17 @@
+#
+# Copyright 2020-2022 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
+#
+# http://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.
+#
+
+io.mybatis.provider.jpa.JakartaJpaEntityTableFactory
diff --git a/jakarta-jpa/src/test/java/io/mybatis/provider/BaseTest.java b/jakarta-jpa/src/test/java/io/mybatis/provider/BaseTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..5abd0642829b8feb1fa8636de7d2eade9687eff6
--- /dev/null
+++ b/jakarta-jpa/src/test/java/io/mybatis/provider/BaseTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2020-2022 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
+ *
+ * http://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 io.mybatis.provider;
+
+import org.apache.ibatis.io.Resources;
+import org.apache.ibatis.jdbc.ScriptRunner;
+import org.apache.ibatis.session.SqlSession;
+import org.apache.ibatis.session.SqlSessionFactory;
+import org.apache.ibatis.session.SqlSessionFactoryBuilder;
+import org.junit.BeforeClass;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.sql.Connection;
+
+public class BaseTest {
+ private static SqlSessionFactory sqlSessionFactory;
+
+ @BeforeClass
+ public static void init() {
+ try {
+ Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
+ sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
+ reader.close();
+
+ //创建数据库
+ try (SqlSession session = sqlSessionFactory.openSession()) {
+ Connection conn = session.getConnection();
+ reader = Resources.getResourceAsReader("testdb.sql");
+ ScriptRunner runner = new ScriptRunner(conn);
+ runner.setLogWriter(null);
+ runner.runScript(reader);
+ reader.close();
+ }
+ } catch (IOException ignore) {
+ }
+ }
+
+ public SqlSession getSqlSession() {
+ return sqlSessionFactory.openSession();
+ }
+
+}
diff --git a/jakarta-jpa/src/test/java/io/mybatis/provider/jpa/BaseProvider.java b/jakarta-jpa/src/test/java/io/mybatis/provider/jpa/BaseProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..9bc8db40428230baaa4af8de54b82a8b06761503
--- /dev/null
+++ b/jakarta-jpa/src/test/java/io/mybatis/provider/jpa/BaseProvider.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2020-2022 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
+ *
+ * http://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 io.mybatis.provider.jpa;
+
+import io.mybatis.provider.EntityColumn;
+import io.mybatis.provider.SqlScript;
+import org.apache.ibatis.builder.annotation.ProviderContext;
+
+import java.util.stream.Collectors;
+
+import static io.mybatis.provider.SqlScript.LF;
+
+public class BaseProvider {
+
+ public static String getById(ProviderContext providerContext) {
+ return SqlScript.caching(providerContext, entity ->
+ "SELECT " + entity.baseColumnAsPropertyList() + " FROM " + entity.tableName() +
+ " WHERE " + entity.idColumns().stream().map(EntityColumn::columnEqualsProperty).collect(Collectors.joining(" AND ")));
+ }
+
+ public static String deleteById(ProviderContext providerContext) {
+ return SqlScript.caching(providerContext, entity ->
+ "DELETE FROM " + entity.tableName() +
+ " WHERE " + entity.idColumns().stream().map(EntityColumn::columnEqualsProperty).collect(Collectors.joining(" AND ")));
+ }
+
+ public static String insertSelective(ProviderContext providerContext) {
+ return SqlScript.caching(providerContext, (entity, util) ->
+ "INSERT INTO " + entity.tableName()
+ + util.trimSuffixOverrides("(", ")", ",", () ->
+ entity.insertColumns().stream().map(column ->
+ util.ifTest(column.notNullTest(), () -> column.column() + ",")
+ ).collect(Collectors.joining(LF)))
+ + util.trimSuffixOverrides(" VALUES (", ")", ",", () ->
+ entity.insertColumns().stream().map(column ->
+ util.ifTest(column.notNullTest(), () -> column.variables() + ",")
+ ).collect(Collectors.joining(LF))));
+ }
+}
diff --git a/jakarta-jpa/src/test/java/io/mybatis/provider/jpa/User.java b/jakarta-jpa/src/test/java/io/mybatis/provider/jpa/User.java
new file mode 100644
index 0000000000000000000000000000000000000000..d146e9366de43064965275f3ddac7d7072bc3385
--- /dev/null
+++ b/jakarta-jpa/src/test/java/io/mybatis/provider/jpa/User.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2020-2022 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
+ *
+ * http://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 io.mybatis.provider.jpa;
+
+import io.mybatis.provider.Entity;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.Id;
+import jakarta.persistence.Table;
+
+@Table(name = "user")
+public class User {
+ @Id
+ @Column
+ private Long id;
+ @Column(name = "name")
+ private String username;
+ @Column
+ @Entity.Column(selectable = false)
+ private String sex;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public String getSex() {
+ return sex;
+ }
+
+ public void setSex(String sex) {
+ this.sex = sex;
+ }
+}
diff --git a/jakarta-jpa/src/test/java/io/mybatis/provider/jpa/UserAuto.java b/jakarta-jpa/src/test/java/io/mybatis/provider/jpa/UserAuto.java
new file mode 100644
index 0000000000000000000000000000000000000000..f3403879edb8c1a8542cffddf2a823454e8f36c2
--- /dev/null
+++ b/jakarta-jpa/src/test/java/io/mybatis/provider/jpa/UserAuto.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2020-2022 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
+ *
+ * http://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 io.mybatis.provider.jpa;
+
+import jakarta.persistence.Id;
+
+public class UserAuto {
+ @Id
+ private int id;
+ private String userName;
+ private String address;
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public String getUserName() {
+ return userName;
+ }
+
+ public void setUserName(String userName) {
+ this.userName = userName;
+ }
+
+ public String getAddress() {
+ return address;
+ }
+
+ public void setAddress(String address) {
+ this.address = address;
+ }
+}
diff --git a/jakarta-jpa/src/test/java/io/mybatis/provider/jpa/UserAutoMapper.java b/jakarta-jpa/src/test/java/io/mybatis/provider/jpa/UserAutoMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..817c0e0356a4c643e9cb7719fc7c08f700574a9c
--- /dev/null
+++ b/jakarta-jpa/src/test/java/io/mybatis/provider/jpa/UserAutoMapper.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2020-2022 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
+ *
+ * http://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 io.mybatis.provider.jpa;
+
+import io.mybatis.provider.Caching;
+import org.apache.ibatis.annotations.Lang;
+import org.apache.ibatis.annotations.SelectProvider;
+
+public interface UserAutoMapper {
+
+ @Lang(Caching.class)
+ @SelectProvider(type = BaseProvider.class, method = "getById")
+ UserAuto getById(Long id);
+
+}
diff --git a/jakarta-jpa/src/test/java/io/mybatis/provider/jpa/UserAutoMapperTest.java b/jakarta-jpa/src/test/java/io/mybatis/provider/jpa/UserAutoMapperTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..01aff1e85394680d864bcb9fb0e82f4ecda604de
--- /dev/null
+++ b/jakarta-jpa/src/test/java/io/mybatis/provider/jpa/UserAutoMapperTest.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2020-2022 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
+ *
+ * http://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 io.mybatis.provider.jpa;
+
+import io.mybatis.provider.BaseTest;
+import org.apache.ibatis.session.SqlSession;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class UserAutoMapperTest extends BaseTest {
+
+ @Test
+ public void testSelectById() {
+ try (SqlSession sqlSession = getSqlSession()) {
+ UserAutoMapper userMapper = sqlSession.getMapper(UserAutoMapper.class);
+ UserAuto user = userMapper.getById(1L);
+ Assert.assertNotNull(user);
+ Assert.assertEquals("sjz", user.getUserName());
+ Assert.assertNotNull(user.getAddress());
+ }
+ }
+
+}
diff --git a/jakarta-jpa/src/test/java/io/mybatis/provider/jpa/UserMapper.java b/jakarta-jpa/src/test/java/io/mybatis/provider/jpa/UserMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..e14ab15c2c9350688555b2c636798467e5654da4
--- /dev/null
+++ b/jakarta-jpa/src/test/java/io/mybatis/provider/jpa/UserMapper.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2020-2022 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
+ *
+ * http://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 io.mybatis.provider.jpa;
+
+import io.mybatis.provider.Caching;
+import org.apache.ibatis.annotations.Lang;
+import org.apache.ibatis.annotations.SelectProvider;
+
+public interface UserMapper {
+
+ @Lang(Caching.class)
+ @SelectProvider(type = BaseProvider.class, method = "getById")
+ User getById(Long id);
+
+}
diff --git a/jakarta-jpa/src/test/java/io/mybatis/provider/jpa/UserMapperTest.java b/jakarta-jpa/src/test/java/io/mybatis/provider/jpa/UserMapperTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..63c8fb47384a467bd28aa39e0f50d563259fd188
--- /dev/null
+++ b/jakarta-jpa/src/test/java/io/mybatis/provider/jpa/UserMapperTest.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2020-2022 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
+ *
+ * http://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 io.mybatis.provider.jpa;
+
+import io.mybatis.provider.BaseTest;
+import org.apache.ibatis.session.SqlSession;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class UserMapperTest extends BaseTest {
+
+ @Test
+ public void testSelectById() {
+ try (SqlSession sqlSession = getSqlSession()) {
+ UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
+ User user = userMapper.getById(1L);
+ Assert.assertNotNull(user);
+ Assert.assertEquals("张无忌", user.getUsername());
+ Assert.assertNull(user.getSex());
+ }
+ }
+
+}
diff --git a/jakarta-jpa/src/test/resources/logback.xml b/jakarta-jpa/src/test/resources/logback.xml
new file mode 100644
index 0000000000000000000000000000000000000000..e47b06cb1404e609b1877d8fa92037789ebf7efd
--- /dev/null
+++ b/jakarta-jpa/src/test/resources/logback.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
diff --git a/jakarta-jpa/src/test/resources/mybatis-config.xml b/jakarta-jpa/src/test/resources/mybatis-config.xml
new file mode 100644
index 0000000000000000000000000000000000000000..4172dde6baadd8debf04bb4fbdb2953fcff8da7c
--- /dev/null
+++ b/jakarta-jpa/src/test/resources/mybatis-config.xml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jakarta-jpa/src/test/resources/testdb.sql b/jakarta-jpa/src/test/resources/testdb.sql
new file mode 100644
index 0000000000000000000000000000000000000000..f4efad11fcc4d9bd72336c76823b5b9605dffba6
--- /dev/null
+++ b/jakarta-jpa/src/test/resources/testdb.sql
@@ -0,0 +1,29 @@
+drop table user if exists;
+drop table user_auto if exists;
+
+create table user
+(
+ id INTEGER GENERATED BY DEFAULT AS IDENTITY (START WITH 1) PRIMARY KEY,
+ name VARCHAR(32) DEFAULT 'DEFAULT',
+ sex VARCHAR(2)
+);
+
+
+insert into user(id, name, sex)
+values (1, '张无忌', '男'),
+ (2, '赵敏', '女'),
+ (3, '周芷若', '女'),
+ (4, '小昭', '女'),
+ (5, '殷离', '女');
+
+-- 自动映射
+create table user_auto
+(
+ id INTEGER GENERATED BY DEFAULT AS IDENTITY (START WITH 1) PRIMARY KEY,
+ user_name VARCHAR(32) DEFAULT 'DEFAULT',
+ address VARCHAR(64)
+);
+insert into user_auto(id, user_name, address)
+values (1, 'sjz', '河北省/石家庄市'),
+ (2, 'hd', '河北省/邯郸市'),
+ (3, 'xt', '河北省/邢台市');