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
相关产品推荐
相关产品推荐

