diff --git a/jOOQ/src/main/java/org/jooq/conf/MiniJAXB.java b/jOOQ/src/main/java/org/jooq/conf/MiniJAXB.java
new file mode 100644
index 0000000000..01341f6727
--- /dev/null
+++ b/jOOQ/src/main/java/org/jooq/conf/MiniJAXB.java
@@ -0,0 +1,317 @@
+/*
+ * 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.
+ *
+ * Other licenses:
+ * -----------------------------------------------------------------------------
+ * Commercial licenses for this work are available. These replace the above
+ * ASL 2.0 and offer limited warranties, support, maintenance, and commercial
+ * database integrations.
+ *
+ * For more information, please visit: http://www.jooq.org/licenses
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ */
+package org.jooq.conf;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.StringReader;
+import java.io.Writer;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.bind.JAXB;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
+import javax.xml.bind.annotation.XmlEnum;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+import javax.xml.bind.annotation.adapters.XmlAdapter;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.jooq.exception.ConfigurationException;
+import org.jooq.exception.ExceptionTools;
+import org.jooq.tools.Convert;
+import org.jooq.tools.JooqLogger;
+import org.jooq.tools.reflect.Reflect;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+
+/**
+ * This class allows for mashalling / unmarshalling XML content to jOOQ
+ * configuration objects.
+ *
+ * With jOOQ 3.12, the JAXB dependency has been removed in favour of this home
+ * grown solution. Due to the modularisation that happened with JDK 9+ and the
+ * removal of JAXB from the JDK 11+, it is unreasonable to leave the burden of
+ * properly configuring transitive JAXB dependency to jOOQ users.
+ *
+ * @author Lukas Eder
+ */
+public class MiniJAXB {
+
+ private static final JooqLogger log = JooqLogger.getLogger(MiniJAXB.class);
+ private static volatile Boolean jaxbAvailable;
+
+ public static void marshal(Object object, OutputStream out) {
+ marshal(object, new OutputStreamWriter(out));
+ }
+
+ public static void marshal(Object object, Writer out) {
+ if (!Boolean.FALSE.equals(jaxbAvailable)) {
+ try {
+ JAXB.marshal(object, out);
+ jaxbAvailable = true;
+ log.debug("JAXB is available from the classpath / module path");
+ return;
+ }
+ catch (Throwable t) {
+ if (ExceptionTools.getCause(t, ClassNotFoundException.class) != null ||
+ ExceptionTools.getCause(t, Error.class) != null) {
+ jaxbAvailable = false;
+ log.debug("JAXB is not available from the classpath / module path");
+ }
+ else {
+ jaxbAvailable = true;
+ log.debug("JAXB is available from the classpath / module path");
+ throw t;
+ }
+ }
+ }
+
+ try {
+ XmlRootElement e = object.getClass().getAnnotation(XmlRootElement.class);
+ if (e != null)
+ out.write("<" + e.name() + ">");
+
+ out.write(object.toString());
+
+ if (e != null)
+ out.write("" + e.name() + ">");
+ }
+ catch (Exception e) {
+ throw new ConfigurationException("Cannot print object", e);
+ }
+ }
+
+ public static T unmarshal(InputStream in, Class type) {
+ return unmarshal0(new InputSource(in), type);
+ }
+
+ public static T unmarshal(String xml, Class type) {
+ return unmarshal0(new InputSource(new StringReader(xml)), type);
+ }
+
+ public static T unmarshal(File xml, Class type) {
+ try {
+ return unmarshal0(new InputSource(new FileInputStream(xml)), type);
+ }
+ catch (Exception e) {
+ throw new ConfigurationException("Error while opening file", e);
+ }
+ }
+
+ private static T unmarshal0(InputSource in, Class type) {
+ if (!Boolean.FALSE.equals(jaxbAvailable)) {
+ try {
+ T result = in.getByteStream() != null
+ ? JAXB.unmarshal(in.getByteStream(), type)
+ : in.getCharacterStream() != null
+ ? JAXB.unmarshal(in.getCharacterStream(), type)
+ : null;
+ jaxbAvailable = true;
+ log.debug("JAXB is available from the classpath / module path");
+ return result;
+ }
+ catch (Throwable t) {
+ if (ExceptionTools.getCause(t, ClassNotFoundException.class) != null ||
+ ExceptionTools.getCause(t, Error.class) != null) {
+ jaxbAvailable = false;
+ log.debug("JAXB is not available from the classpath / module path");
+ }
+ else {
+ jaxbAvailable = true;
+ log.debug("JAXB is available from the classpath / module path");
+ throw t;
+ }
+ }
+ }
+
+ try {
+ Document document = builder().parse(in);
+ T result = Reflect.on(type).create().get();
+ unmarshal0(result, document.getDocumentElement());
+ return result;
+ }
+ catch (Exception e) {
+ throw new ConfigurationException("Error while reading xml", e);
+ }
+ }
+
+ private static void unmarshal0(Object result, Element element) throws Exception {
+ if (result == null)
+ return;
+
+ Class> type = result.getClass();
+ for (Field child : type.getDeclaredFields()) {
+ int modifiers = child.getModifiers();
+ if (Modifier.isFinal(modifiers) ||
+ Modifier.isStatic(modifiers))
+ continue;
+
+ XmlElementWrapper w = child.getAnnotation(XmlElementWrapper.class);
+ XmlElement e = child.getAnnotation(XmlElement.class);
+ XmlJavaTypeAdapter a = child.getAnnotation(XmlJavaTypeAdapter.class);
+
+ String childName = child.getName();
+ String childElementName =
+ w != null
+ ? "##default".equals(w.name())
+ ? child.getName()
+ : w.name()
+ : e == null || "##default".equals(e.name())
+ ? childName
+ : e.name();
+
+ Element childElement = child(element, childElementName);
+ if (childElement == null)
+ continue;
+
+ Class> childType = child.getType();
+ if (List.class.isAssignableFrom(childType) && w != null && e != null) {
+ List