背景:
在项目开发中,每个mapper xml都存在大量重复的基础CRUD操作,如 selectById、deleteById 等。这些模板化的SQL语句不仅增加了开发工作量,还容易出现不一致的问题。
解决方案:
通过动态解析和修改Mybatis XML文件,自动注入缺失的通用SQL语句,实现代码复用和标准化。
核心实现:
1.设置注入后的xml
1 2 3 4 5 6 7 8 9 10 11
| @Bean public SqlSessionFactory sqlSessionFactoryBean(DataSource dataSource) throws Exception { SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); sessionFactory.setDataSource(dataSource); List<Resource> resources = new ArrayList<>(); for (Resource resource : mybatisProperties.resolveMapperLocations()){ resources.add(MyBatisXmlProcessor.parse(resource)); } sessionFactory.setMapperLocations(resources.toArray(new Resource[0])); return sessionFactory.getObject(); }
|
2.预定义通用SQL模板,XML解析与语句注入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
| @Slf4j public class MyBatisXmlProcessor { private static final Map<String, String> DEFAULT_STATEMENTS = new HashMap<>();
static { DEFAULT_STATEMENTS.put("selectById", "<select id=\"selectById\" resultMap=\"BaseResultMap\">\n" + " select * from <include refid=\"BaseTable\"/> where id = #{id}\n" + "</select>"); }
public static Resource parse(Resource resource) { try {
Set<String> existingIds = getExistingStatementIds(resource);
Map<String, String> statementsToAdd = DEFAULT_STATEMENTS.entrySet().stream().filter(entry -> !existingIds.contains(entry.getKey())).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
if (statementsToAdd.isEmpty()) { return resource; }
String modifiedXml = insertStatementsBeforeMapperEnd(resource, statementsToAdd);
log.info("Modified XML:\n{}", modifiedXml);
return new ByteArrayResource(modifiedXml.getBytes(StandardCharsets.UTF_8)); } catch (Exception e) { throw new RuntimeException("Failed to parse and modify MyBatis XML", e); } }
private static Set<String> getExistingStatementIds(Resource resource) throws ParserConfigurationException, IOException, SAXException {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(resource.getInputStream()); Element root = document.getDocumentElement();
if (!"mapper".equals(root.getTagName())) { throw new IllegalArgumentException("Invalid MyBatis XML: root element is not <mapper>"); }
NodeList children = root.getChildNodes();
return IntStream.range(0, children.getLength()) .mapToObj(children::item) .filter(node -> node.getNodeType() == Node.ELEMENT_NODE) .map(node -> ((Element) node).getAttribute("id")) .filter(id -> id != null && !id.isEmpty()) .collect(Collectors.toSet()); }
private static String insertStatementsBeforeMapperEnd(Resource resource, Map<String, String> statementsToAdd) throws IOException { String xmlContent = StreamUtils.copyToString(resource.getInputStream(), StandardCharsets.UTF_8); int mapperEndIndex = xmlContent.lastIndexOf("</mapper>"); if (mapperEndIndex == -1) { throw new IllegalArgumentException("Invalid MyBatis XML: </mapper> tag not found"); }
StringBuilder modifiedXml = new StringBuilder();
modifiedXml.append(xmlContent, 0, mapperEndIndex);
statementsToAdd.values().forEach(o-> modifiedXml.append(o).append("\n"));
modifiedXml.append(xmlContent.substring(mapperEndIndex));
return modifiedXml.toString(); } }
|