You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Data JPA中Oracle XMLType映射为实体类HashMap的实现方案

实现Spring Data JPA映射XMLType到Map<String,String>的方法

可以通过JPA的AttributeConverter接口实现自定义类型转换,将XMLType列与实体类中的Map<String,String>字段双向映射,核心是编写转换器处理XML与Map的互转。

方法一:使用DOM API手动解析生成XML

适合需要完全控制XML处理逻辑的场景,无需额外依赖(除了JDBC和XML解析API)。

1. 编写AttributeConverter实现类

import oracle.xdb.XMLType;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;

@Converter(autoApply = false)
public class LangMapToXmlTypeConverter implements AttributeConverter<Map<String, String>, XMLType> {

    // Map转XMLType:生成符合结构的XML字符串再转为XMLType
    @Override
    public XMLType convertToDatabaseColumn(Map<String, String> attribute) {
        if (attribute == null || attribute.isEmpty()) {
            return null;
        }
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document doc = builder.newDocument();

            // 创建根节点<values>
            Element root = doc.createElement("values");
            doc.appendChild(root);

            // 遍历Map生成<value>节点
            for (Map.Entry<String, String> entry : attribute.entrySet()) {
                Element valueElement = doc.createElement("value");
                valueElement.setAttribute("lang", entry.getKey());
                valueElement.setTextContent(entry.getValue());
                root.appendChild(valueElement);
            }

            // 将DOM转为XML字符串
            TransformerFactory transformerFactory = TransformerFactory.newInstance();
            Transformer transformer = transformerFactory.newTransformer();
            StringWriter writer = new StringWriter();
            transformer.transform(new DOMSource(doc), new StreamResult(writer));
            String xmlString = writer.toString();

            return XMLType.createXML(xmlString);
        } catch (Exception e) {
            throw new RuntimeException("Map转XMLType失败", e);
        }
    }

    // XMLType转Map:解析XML提取lang属性和文本内容
    @Override
    public Map<String, String> convertToEntityAttribute(XMLType dbData) {
        if (dbData == null) {
            return new HashMap<>();
        }
        try {
            Document doc = dbData.getDocument();
            NodeList valueNodes = doc.getElementsByTagName("value");
            Map<String, String> langMap = new HashMap<>();

            for (int i = 0; i < valueNodes.getLength(); i++) {
                Element valueElement = (Element) valueNodes.item(i);
                String lang = valueElement.getAttribute("lang");
                String text = valueElement.getTextContent().trim();
                langMap.put(lang, text);
            }

            return langMap;
        } catch (Exception e) {
            throw new RuntimeException("XMLType转Map失败", e);
        }
    }
}

2. 在实体类中应用转换器

import jakarta.persistence.*;
import java.util.Map;

@Entity
@Table(name = "your_table_name")
public class YourEntity {

    // 主键及其他字段
    @Id
    private Long id;

    // 映射XMLType列,指定转换器
    @Column(name = "xml_column_name", columnDefinition = "XMLType")
    @Convert(converter = LangMapToXmlTypeConverter.class)
    private Map<String, String> langValues;

    // Getter、Setter方法
    public Map<String, String> getLangValues() {
        return langValues;
    }

    public void setLangValues(Map<String, String> langValues) {
        this.langValues = langValues;
    }
}

方法二:使用Jackson XML模块简化处理

如果项目中已经引入Jackson,可借助其XML模块快速实现转换,代码更简洁。

1. 引入Jackson XML依赖

<!-- Maven依赖 -->
<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
</dependency>

2. 编写带Jackson注解的辅助类

import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
import java.util.Map;

@JacksonXmlRootElement(localName = "values")
public class LangValuesWrapper {
    private Map<String, String> value;

    @JacksonXmlProperty(localName = "value")
    @JacksonXmlElementWrapper(useWrapping = false) // 禁用额外的包装节点
    public Map<String, String> getValue() {
        return value;
    }

    public void setValue(Map<String, String> value) {
        this.value = value;
    }
}

3. 编写基于Jackson的转换器

import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import oracle.xdb.XMLType;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;

import java.util.HashMap;
import java.util.Map;

@Converter(autoApply = false)
public class LangMapToXmlTypeJacksonConverter implements AttributeConverter<Map<String, String>, XMLType> {

    private static final XmlMapper xmlMapper = new XmlMapper();

    static {
        xmlMapper.configure(com.fasterxml.jackson.dataformat.xml.XmlParser.Feature.IGNORE_UNKNOWN_PROPERTIES, true);
    }

    @Override
    public XMLType convertToDatabaseColumn(Map<String, String> attribute) {
        if (attribute == null || attribute.isEmpty()) {
            return null;
        }
        try {
            LangValuesWrapper wrapper = new LangValuesWrapper();
            wrapper.setValue(attribute);
            String xmlString = xmlMapper.writeValueAsString(wrapper);
            return XMLType.createXML(xmlString);
        } catch (Exception e) {
            throw new RuntimeException("Map转XMLType失败", e);
        }
    }

    @Override
    public Map<String, String> convertToEntityAttribute(XMLType dbData) {
        if (dbData == null) {
            return new HashMap<>();
        }
        try {
            String xmlString = dbData.getStringVal();
            LangValuesWrapper wrapper = xmlMapper.readValue(xmlString, LangValuesWrapper.class);
            return wrapper.getValue() != null ? wrapper.getValue() : new HashMap<>();
        } catch (Exception e) {
            throw new RuntimeException("XMLType转Map失败", e);
        }
    }
}

4. 在实体类中应用该转换器

只需将@Convert注解中的转换器替换为LangMapToXmlTypeJacksonConverter即可。

注意事项

  • 如果使用非Oracle数据库(如PostgreSQL、MySQL),其XML列通常存储为字符串类型,只需将转换器中的XMLType替换为String,并调整数据库列的columnDefinition为对应数据库的XML类型(如PostgreSQL的xml、MySQL的TEXT或XML)。
  • 异常处理可根据项目需求替换为自定义业务异常,避免抛出RuntimeException。
  • 若需要全局自动应用该转换器,可将@Converter注解的autoApply属性设为true。

内容的提问来源于stack exchange,提问作者Rushikesh Sabde

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 21:15:42