package jef.database; import java.io.File; import java.io.IOException; import java.net.URL; import java.sql.SQLException; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import jef.common.log.LogUtil; import jef.database.Condition.Operator; import jef.database.query.Query; import jef.database.support.RDBMS; import jef.tools.Assert; import jef.tools.IOUtils; import jef.tools.StringUtils; import jef.tools.XMLUtils; import jef.tools.reflect.Enums; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.xml.sax.SAXException; final class NamedQueryHolder { private DbClient parent; private Map<String, NQEntry> namedQueries; private Map<File, Long> loadedFiles = new HashMap<File, Long>(); private long lastUpdate;// 记录上次更新文件的时间 public NamedQueryHolder(DbClient parent) { this.parent = parent; initQueries(); lastUpdate = System.currentTimeMillis(); } public NQEntry get(String name) { if (ORMConfig.getInstance().isCheckUpdateForNamedQueries()) {// 允许动态修改SQL查询 synchronized (this) { if (System.currentTimeMillis() - lastUpdate > 10000) {// 十秒内不更新修改 checkUpdate(name); lastUpdate = System.currentTimeMillis(); } } } return namedQueries.get(name); } private void put0(Map<String, NQEntry> namedQueries, NamedQueryConfig namedQueryConfig, RDBMS dialect, String source) { String name = namedQueryConfig.getName(); NQEntry entry = namedQueries.get(name); if (entry == null) { namedQueries.put(name, new NQEntry(namedQueryConfig, dialect, source)); return; } // 尝试替代原有的数据 { NQEntry current = entry; while (current != null) { if (dialect == current.dialect) { // 告警 String type = dialect == null ? "*" : dialect.name(); LogUtil.warn("The Named-Query [{}] for {} in [{]}, was replaced by duplicate config from [{}]", name, type, current.getSource(), source); current.config = namedQueryConfig; return; } current = current.next; } } // 原有数据没有重复的,添加到链表 NQEntry newElement = new NQEntry(namedQueryConfig, dialect, source); if (dialect == null) { newElement.next = entry; namedQueries.put(name, newElement); } else { NQEntry current = entry; while (current.next != null) { current = current.next; } current.next = newElement; } } // 检查文件更新 public void checkUpdate(String name) { // 先通过文件日期检查更新 for (Map.Entry<File, Long> e : loadedFiles.entrySet()) { File file = e.getKey(); if (file.lastModified() > e.getValue()) {// 修改过了 LogUtil.show("refresh named queries in file <" + file.toString() + ">"); loadFile(namedQueries, file); } } // 尝试获取 String tablename = parent.getNamedQueryTable(); if (StringUtils.isNotEmpty(tablename)) { NQEntry e = null; if (StringUtils.isNotEmpty(name)) { e = namedQueries.get(name); } if (e == null) {// 全刷 try { Query<NamedQueryConfig> q = QB.create(NamedQueryConfig.class); q.setCustomTableName(tablename); List<NamedQueryConfig> dbQueries = parent.select(q, null); for (NamedQueryConfig qc : dbQueries) { if (StringUtils.isEmpty(qc.getName())) { continue; } qc.stopUpdate(); qc.setFromDb(true); RDBMS type = processName(qc); put0(namedQueries, qc, type, "database"); } } catch (SQLException ex) { LogUtil.exception(ex); } } else { // 单刷 NamedQueryConfig config = e.config; if (config.isFromDb()) {// 到数据库去载入 NamedQueryConfig q = new NamedQueryConfig(); q.getQuery().addCondition(NamedQueryConfig.Field.name, Operator.MATCH_START, name); try { for (NamedQueryConfig nq : parent.select(q)) { RDBMS type = processName(nq); put0(namedQueries, q, type, "database"); } } catch (SQLException e1) { LogUtil.exception(e1); } } } } } /** * 从名称中提取出RDBMS * * @param q * @return */ private RDBMS processName(NamedQueryConfig q) { String name = q.getName(); Assert.notNull(name); int n = name.indexOf('#'); if (n > -1) { String type = name.substring(n + 1).toLowerCase(); RDBMS rtype = Enums.valueOf(RDBMS.class, type, "The Database type in namedquery [%s] is unknown.", name); name = name.substring(0, n); q.setName(name); return rtype; } else { return null; } } // 初始化全部查询 private synchronized void initQueries() { if (namedQueries != null) return; Map<String, NQEntry> result = new ConcurrentHashMap<String, NQEntry>(); boolean debugMode = ORMConfig.getInstance().isDebugMode(); String filename=parent.getNamedQueryFile(); if (StringUtils.isNotEmpty(filename)) { try { Enumeration<URL> urls=getClass().getClassLoader().getResources(filename); // Load from files for (;urls.hasMoreElements();) { URL queryFile =urls.nextElement(); if (queryFile == null) continue; if (debugMode) { LogUtil.show("loading named queries from file <" + queryFile.toString() + ">"); } File file = IOUtils.urlToFile(queryFile); loadFile(result, file); } } catch (IOException e) { LogUtil.exception(e); } } if (StringUtils.isNotEmpty(parent.getNamedQueryTable())) { String tablename = parent.getNamedQueryTable(); try { if (debugMode) { LogUtil.show("loading named queries in table <" + tablename + ">"); } Query<NamedQueryConfig> q = QB.create(NamedQueryConfig.class); q.setCustomTableName(tablename); List<NamedQueryConfig> dbQueries = parent.select(q, null); for (NamedQueryConfig qc : dbQueries) { if (StringUtils.isEmpty(qc.getName())) { continue; } qc.stopUpdate(); qc.setFromDb(true); RDBMS type = processName(qc); put0(result, qc, type, "database"); } } catch (SQLException e) { LogUtil.exception(e); } } this.namedQueries = result; } private synchronized void loadFile(Map<String, NQEntry> result, File file) { loadedFiles.put(file, file.lastModified()); try { Document doc = XMLUtils.loadDocument(file); String namespace=doc.getDocumentElement().getAttribute("namespace"); for (Element e : XMLUtils.childElements(doc.getDocumentElement(), "query")) { String name = XMLUtils.attrib(e, "name"); String type = XMLUtils.attrib(e, "type"); String sql = XMLUtils.nodeText(e); int size = StringUtils.toInt(XMLUtils.attrib(e, "fetch-size"), 0); // int max=StringUtils.toInt(XMLUtils.attrib(e, "max-rows"), 0); if(StringUtils.isNotEmpty(namespace)){ name=namespace+"."+name; } NamedQueryConfig nq = new NamedQueryConfig(name, sql, "JPQL".equalsIgnoreCase(type), size); nq.setTag(XMLUtils.attrib(e, "tag")); RDBMS dialect = processName(nq); put0(result, nq, dialect, file.getAbsolutePath()); } } catch (SAXException e) { LogUtil.exception(e); } catch (IOException e) { LogUtil.exception(e); } } }