Skip to content

Instantly share code, notes, and snippets.

@wendal
Created January 3, 2015 14:15
Show Gist options
  • Select an option

  • Save wendal/14a0a84b4dbf280e6faa to your computer and use it in GitHub Desktop.

Select an option

Save wendal/14a0a84b4dbf280e6faa to your computer and use it in GitHub Desktop.

Revisions

  1. wendal created this gist Jan 3, 2015.
    33 changes: 33 additions & 0 deletions IotUser.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,33 @@
    package org.nutz.dao.impl.entity.xml;

    public class IotUser {

    private long id;
    private String name;
    private int age;
    public int getAge() {
    return age;
    }
    public void setAge(int age) {
    this.age = age;
    }
    private String location;
    public long getId() {
    return id;
    }
    public void setId(long id) {
    this.id = id;
    }
    public String getName() {
    return name;
    }
    public void setName(String name) {
    this.name = name;
    }
    public String getLocation() {
    return location;
    }
    public void setLocation(String location) {
    this.location = location;
    }
    }
    456 changes: 456 additions & 0 deletions XmlEntityMaker.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,456 @@
    package org.nutz.dao.impl.entity.xml;

    import java.io.IOException;
    import java.io.InputStream;
    import java.lang.reflect.Field;
    import java.sql.Connection;
    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;
    import java.util.Map;
    import java.util.Map.Entry;
    import java.util.concurrent.ConcurrentHashMap;

    import javax.sql.DataSource;
    import javax.xml.parsers.DocumentBuilder;
    import javax.xml.parsers.ParserConfigurationException;

    import org.nutz.dao.DB;
    import org.nutz.dao.DaoException;
    import org.nutz.dao.entity.Entity;
    import org.nutz.dao.entity.EntityMaker;
    import org.nutz.dao.entity.MacroType;
    import org.nutz.dao.entity.MappingField;
    import org.nutz.dao.entity.annotation.ColType;
    import org.nutz.dao.impl.EntityHolder;
    import org.nutz.dao.impl.entity.FieldMacroInfo;
    import org.nutz.dao.impl.entity.NutEntity;
    import org.nutz.dao.impl.entity.NutEntityIndex;
    import org.nutz.dao.impl.entity.field.ManyLinkField;
    import org.nutz.dao.impl.entity.field.ManyManyLinkField;
    import org.nutz.dao.impl.entity.field.NutMappingField;
    import org.nutz.dao.impl.entity.field.OneLinkField;
    import org.nutz.dao.impl.entity.info.LinkInfo;
    import org.nutz.dao.impl.entity.macro.ElFieldMacro;
    import org.nutz.dao.impl.entity.macro.SqlFieldMacro;
    import org.nutz.dao.jdbc.JdbcExpert;
    import org.nutz.dao.jdbc.Jdbcs;
    import org.nutz.dao.sql.Pojo;
    import org.nutz.lang.Lang;
    import org.nutz.lang.Strings;
    import org.nutz.lang.Xmls;
    import org.nutz.log.Log;
    import org.nutz.log.Logs;
    import org.nutz.resource.NutResource;
    import org.nutz.resource.Scans;
    import org.nutz.trans.Trans;
    import org.w3c.dom.Document;
    import org.w3c.dom.Element;
    import org.xml.sax.SAXException;

    /**
    * 基于XML配置Entity
    * @author wendal([email protected])
    *
    */
    @SuppressWarnings({"unchecked", "rawtypes"})
    public class XmlEntityMaker implements EntityMaker {

    private static final Log log = Logs.get();

    Map<String, Entity> map = new ConcurrentHashMap<String, Entity>();

    DocumentBuilder builder;

    private DataSource datasource;

    private JdbcExpert expert;

    private EntityHolder holder;

    protected Map<String, List<Element>> pendingLinks = new ConcurrentHashMap<String, List<Element>>();

    public XmlEntityMaker(DataSource datasource, JdbcExpert expert, EntityHolder holder) throws ParserConfigurationException {
    builder = Lang.xmls();
    this.datasource = datasource;
    this.expert = expert;
    this.holder = holder;
    }

    public <T> Entity<T> make(Class<T> type) {
    return map.get(type); // 注意, 这里是单纯从缓存中获取已有的Entity, 因为是基于XML的
    }

    public void setPaths(String ... paths) throws IOException, SAXException {
    for (String path : paths) {
    addPath(path);
    }
    try {
    verify();
    } catch (DaoException e) {
    log.info("some relation pending are not complete", e);
    }
    }

    public void addPath(String path) throws IOException, SAXException {
    List<NutResource> files = Scans.me().scan(path, ".xml$");
    if (files.isEmpty())
    return;
    for (NutResource resource : files) {
    log.debug("load entity xml --> " + resource.getName());
    add(resource.getInputStream());
    }
    }

    public void add(InputStream ins) throws IOException, SAXException {
    add(builder.parse(ins));
    }

    public void add(Document document) {
    document.normalizeDocument();
    Element top = document.getDocumentElement();
    if (!top.getNodeName().endsWith("nutz-mapping")) { // 没有nutz-mapping, 自然没啥好说的了
    log.info("skip xml without nutz-mapping");
    return;
    }
    String topPackageName = top.getAttribute("package");
    for (Element ele : Xmls.children(top, "entity")) { // 一个XML中允许包含多个entity描述
    addXmlEntity(ele, topPackageName);
    }
    }

    public void addXmlEntity(final Element ele, String topPackageName) {
    String type = ele.getAttribute("type");
    if (Strings.isBlank(type)) // xsd有约束,这里再检查一次
    throw new DaoException("entity without type!!");
    Class<?> klass = null;
    try {
    klass = loadClass(topPackageName, type); // 尝试载入对应的类
    } catch (ClassNotFoundException e) {
    throw new DaoException("entity type ClassNotFound : " + type, e);
    }
    NutEntity<?> en = new NutEntity(klass);
    String tableName = null;
    if (Strings.isBlank(ele.getAttribute("table"))) {
    tableName = Strings.lowerWord(klass.getSimpleName(), '_');
    log.warnf("No @Table found, fallback to use table name='%s' for type '%s'", tableName, klass.getName());
    } else {
    tableName = ele.getAttribute("table");
    }
    String viewName = Strings.isBlank(ele.getAttribute("view")) ? tableName : ele.getAttribute("view");
    en.setTableName(tableName);
    en.setViewName(viewName);

    // 表注解
    String tableComment = ele.getAttribute("comment");
    if (!Strings.isBlank(tableComment))
    en.setTableComment(tableComment);

    // TODO 支持default, readonly 注解所对应的xml配置

    // 先塞进去, 主要是为了避免1对1的自我映射
    holder.set(en);
    map.put(type, en);

    // 下面开始处理字段,索引,等等
    for (Element e : Xmls.children(ele)) {
    String ename = e.getNodeName();
    if ("id".equals(ename)) {
    addField(en, e, true, false);
    } else if ("name".equals(ename)) {
    addField(en, e, false, true);
    } else if ("field".equals(ename)) {
    addField(en, e, false, false);
    } else if ("index".equals(ename)) {
    String indexName = e.getAttribute("name");
    if (Strings.isBlank(indexName)) {
    throw new DaoException("index must have a name, entity=" + type);
    }
    String indexFields = e.getAttribute("fields");
    if (Strings.isBlank(indexFields)) {
    throw new DaoException("index must have fields, entity=" + type);
    }
    boolean indexUnique = "true".equals(e.getAttribute("unique"));
    NutEntityIndex eIndex = new NutEntityIndex();
    eIndex.setName(ename);
    eIndex.setUnique(indexUnique);
    String[] names = Strings.splitIgnoreBlank(indexFields, ",");
    for (String fieldName : names) {
    MappingField field = en.getField(fieldName);
    if (field == null)
    throw new DaoException("index refer not-exist field, entity=" + type + ", field=" + fieldName);
    en.addIndex(eIndex);
    }
    } else if ("one".equals(ename) || "many".equals(ename) || "manymany".equals(ename)) {
    String target = e.getAttribute("target");
    Class<?> targetClass = null;;
    try {
    targetClass = loadClass(topPackageName, target);
    } catch (ClassNotFoundException e1) {
    throw new DaoException("relation ClassNotFound entity=" + en.getType().getName() + ", relation class=" + target);
    }
    Entity relationEntity = map.get(targetClass);
    if (relationEntity == null) {
    List<Element> list = pendingLinks.get(en.getType().getName());
    if (list == null) {
    list = new ArrayList<Element>();
    pendingLinks.put(en.getType().getName(), list);
    log.debug("add pending relation mapping " + e);
    }
    e.setAttribute("fullClassName", targetClass.getName());
    list.add(e);
    } else {
    addRelation(en, relationEntity, e);
    }
    }
    }

    // 处理一下复合主键
    String pks = ele.getAttribute("pks");
    if (!Strings.isBlank(pks)) {
    String[] tmp = Strings.splitIgnoreBlank(pks, ",");
    en.checkCompositeFields(tmp);
    }

    if (null != datasource && null != expert) {
    _checkupEntityFieldsWithDatabase(en);
    }
    }

    public void verify() throws DaoException {
    if (pendingLinks.isEmpty())
    return;
    for (Entry<String, List<Element>> pending : pendingLinks.entrySet()) {
    try {
    NutEntity en = (NutEntity) map.get(loadClass(null, pending.getKey()));
    Iterator<Element> it = pending.getValue().iterator();
    while (it.hasNext()) {
    Element element = it.next();
    NutEntity relation = (NutEntity) map.get(loadClass(null, element.getAttribute("fullClassName")));
    addRelation(en, relation, element);
    it.remove();
    }
    } catch (ClassNotFoundException e) {
    throw Lang.impossible();
    }
    pendingLinks.remove(pending.getKey());
    }
    }

    protected void addRelation(NutEntity<?> en, Entity relationEntity, Element e) {
    LinkInfo info = new LinkInfo();
    info.name = e.getAttribute("name");
    try {
    info.fieldType = en.getMirror().getField(info.name).getType();
    } catch (NoSuchFieldException e1) {
    throw new DaoException("field not exist. entity=" + en.getType() + ",field=" + info.name);
    }
    info.injecting = en.getMirror().getInjecting(info.name);
    info.ejecting = en.getMirror().getEjecting(info.name);

    String rname = e.getNodeName();
    if ("one".equals(rname) || "many".equals(rname)) {
    String fieldName = e.getAttribute("field");
    MappingField mf = en.getField(fieldName);
    if (fieldName == null || mf == null) {
    throw new DaoException(String.format("%s <--> %s by field(%s) , but not exist", en.getType(), relationEntity.getType(), fieldName));
    }
    String key = e.getAttribute("key");
    MappingField mfKey = null;
    if (!Strings.isBlank(key)) {
    mfKey = relationEntity.getField(key);
    } else {
    mfKey = mf.getTypeMirror().isIntLike() ? relationEntity.getIdField() : relationEntity.getNameField();
    }
    if (mfKey == null) {
    throw new DaoException(String.format("%s <--> %s by field(%s) , but not exist", en.getType(), relationEntity.getType(), mfKey));
    }

    if ("one".equals(rname)) {
    OneLinkField one = new OneLinkField(en, holder, info, relationEntity.getClass(), mf, mfKey);
    en.addLinkField(one);
    } else {
    ManyLinkField many = new ManyLinkField(en, holder, info, relationEntity.getClass(), mf, mfKey);
    en.addLinkField(many);
    }
    } else if ("manymany".equals(rname)) {
    // 多对多
    String from = e.getAttribute("from");
    String to = e.getAttribute("to");
    String relation = e.getAttribute("relation");
    String key = e.getAttribute("key");
    ManyManyLinkField manymany = new ManyManyLinkField(en, holder, info, relationEntity.getType(), from, to, relation, key);
    en.addLinkField(manymany);
    }
    }

    protected void addField(NutEntity en, Element ele, boolean isId, boolean isName) {
    NutMappingField mf = new NutMappingField(en);
    // 处理name属性
    String name = ele.getAttribute("name");
    if (isId) {
    mf.setAsId();
    if (!"false".equals(ele.getAttribute("auto")))
    mf.setAsAutoIncreasement();
    if (Strings.isBlank(name))
    mf.setName("id");
    else
    mf.setName(name);
    } else if (isName) {
    mf.setAsName();
    if (Strings.isBlank(name))
    mf.setName("name");
    else
    mf.setName(name);
    } else {
    if (Strings.isBlank(name))
    throw new DaoException("field must have a name attribute, entity=" + en.getType().getName());
    mf.setName(name);
    }
    name = mf.getName();
    try {
    Field field = en.getMirror().getField(name);
    mf.setType(field.getType());
    } catch (NoSuchFieldException e) {
    throw new DaoException("not such field in entity=" + en.getType().getName() + ", field=" + name);
    }

    // 处理column属性
    String columnName = ele.getAttribute("column");
    if (!Strings.isBlank(columnName))
    mf.setColumnName(columnName);
    else
    mf.setColumnName(name);

    // 处理comment属性
    if (!Strings.isBlank(ele.getAttribute("comment"))) {
    mf.setColumnComment(ele.getAttribute("comment"));
    en.setHasColumnComment(true);
    }

    // 处理一下coldefine, prev, next节点
    boolean flag = true;
    for (Element e : Xmls.children(ele)) {
    String ename = e.getNodeName();
    if ("coldefine".equalsIgnoreCase(ename)) {
    flag = false;
    addColDefine(mf, e);
    } else if ("prev".equalsIgnoreCase(ename)) {
    _makeMacro(en, mf, e, true);
    } else if ("next".equals(ename)) {
    _makeMacro(en, mf, e, false);
    }
    }

    if (flag) {
    Jdbcs.guessEntityFieldColumnType(mf);
    }

    // 字段值的适配器
    if (expert != null)
    mf.setAdaptor(expert.getAdaptor(mf));

    mf.setInjecting(en.getMirror().getInjecting(name));
    mf.setEjecting(en.getMirror().getEjecting(name));

    en.addMappingField(mf);
    }

    protected void addColDefine(NutMappingField mf, Element ele) {
    if (hasAttr(ele, "type"))
    mf.setColumnType(ColType.valueOf(ele.getAttribute("type")));
    if (hasAttr(ele, "width"))
    mf.setWidth(Integer.parseInt(ele.getAttribute("width")));
    if (mf.getWidth() == 0 && mf.getColumnType() == ColType.VARCHAR)
    mf.setWidth(50);

    if (hasAttr(ele, "precision"))
    mf.setPrecision(Integer.parseInt(ele.getAttribute("precision")));
    if ("true".equals(ele.getAttribute("notNull")))
    mf.setAsNotNull();
    if ("true".equals(ele.getAttribute("unsigned")))
    mf.setAsUnsigned();
    // 无视auto设置
    // --------------
    if (hasAttr(ele, "customType"))
    mf.setCustomDbType(ele.getAttribute("customType"));
    if ("false".equals(ele.getAttribute("insert")))
    mf.setInsert(false);
    if ("false".equals(ele.getAttribute("update")))
    mf.setUpdate(false);

    }

    protected boolean hasAttr(Element ele, String attrName) {
    return !Strings.isBlank(ele.getAttribute(attrName));
    }

    protected void _makeMacro(NutEntity en, NutMappingField mf, Element ele, boolean isPrev) {
    List<FieldMacroInfo> list = new ArrayList<FieldMacroInfo>();
    for(Element e : Xmls.children(ele)) {
    String nodeName = e.getNodeName();
    DB db = hasAttr(e, "db") ? DB.OTHER : DB.valueOf(e.getAttribute("db"));
    if ("sql".equals(nodeName)) {
    FieldMacroInfo sql = new FieldMacroInfo(MacroType.SQL, db, Xmls.getText(e));
    list.add(sql);
    } else if ("el".equals(nodeName)) {
    FieldMacroInfo el = new FieldMacroInfo(MacroType.EL, db, Xmls.getText(e));
    list.add(el);
    }
    };
    if (isPrev)
    en.addBeforeInsertMacro(__macro(mf, list));
    else
    en.addAfterInsertMacro(__macro(mf, list));
    }

    private void _checkupEntityFieldsWithDatabase(NutEntity<?> en) {
    Connection conn = null;
    try {
    conn = Trans.getConnectionAuto(datasource);
    expert.setupEntityField(conn, en);
    }
    catch (Exception e) {
    if (log.isDebugEnabled())
    log.debugf("Fail to setup '%s'(%s) by DB, because: (%s)'%s'",
    en.getType().getName(),
    en.getTableName(),
    e.getClass().getName(),
    e.getMessage());
    }
    finally {
    Trans.closeConnectionAuto(conn);
    }
    }

    private Pojo __macro(MappingField ef, List<FieldMacroInfo> infoList) {
    FieldMacroInfo theInfo = null;
    // 根据当前数据库,找到合适的宏
    for (FieldMacroInfo info : infoList) {
    if (DB.OTHER == info.getDb()) {
    theInfo = info;
    } else if (info.getDb().name().equalsIgnoreCase(expert.getDatabaseType())) {
    theInfo = info;
    break;
    }
    }
    // 如果找到,增加
    if (null != theInfo) {
    if (theInfo.isEl())
    return new ElFieldMacro(ef, theInfo.getValue());
    else
    return new SqlFieldMacro(ef, theInfo.getValue());
    }
    return null;
    }

    protected Class<?> loadClass(String topPackageName, String type) throws ClassNotFoundException {
    try {
    return Class.forName(type);
    } catch (ClassNotFoundException e) {
    if (!Strings.isBlank(topPackageName))
    return Class.forName(topPackageName + "." + type);
    throw e;
    }
    }
    }
    216 changes: 216 additions & 0 deletions nutz-dao-0.1.xsd
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,216 @@
    <?xml version="1.0" encoding="UTF-8"?>
    <xsd:schema xmlns="http://nutzam.com/schema/entities" targetNamespace="http://nutzam.com/schema/entities" xmlns:xsd="http://www.w3.org/2001/XMLSchema">

    <xsd:element name="nutz-mapping">
    <xsd:complexType>
    <xsd:sequence>
    <xsd:element name="entity" maxOccurs="unbounded" minOccurs="0">
    <xsd:complexType>
    <xsd:sequence>
    <xsd:element name="id" maxOccurs="1" minOccurs="0">
    <xsd:complexType>
    <xsd:sequence>
    <xsd:element name="coldefine" maxOccurs="1" minOccurs="0">
    <xsd:complexType>
    <xsd:attribute name="type" type="xsd:string"></xsd:attribute>
    <xsd:attribute name="width" type="xsd:integer"></xsd:attribute>
    <xsd:attribute name="precision" type="xsd:integer"></xsd:attribute>
    <xsd:attribute name="notNull" type="xsd:boolean"></xsd:attribute>
    <xsd:attribute name="unsigned" type="xsd:boolean"></xsd:attribute>
    <xsd:attribute name="auto" type="xsd:boolean"></xsd:attribute>
    <xsd:attribute name="customType"></xsd:attribute>
    <xsd:attribute name="insert" type="xsd:boolean"></xsd:attribute>
    <xsd:attribute name="update" type="xsd:boolean"></xsd:attribute>
    </xsd:complexType>
    </xsd:element>
    <xsd:element name="prev" maxOccurs="unbounded" minOccurs="0">
    <xsd:complexType>
    <xsd:sequence>
    <xsd:element name="sql" minOccurs="0" maxOccurs="unbounded">
    <xsd:complexType>
    <xsd:attribute name="db"></xsd:attribute>
    </xsd:complexType>
    </xsd:element>
    <xsd:element name="el" minOccurs="0" maxOccurs="unbounded">
    <xsd:complexType>
    <xsd:attribute name="db"></xsd:attribute>
    </xsd:complexType>
    </xsd:element>
    </xsd:sequence>
    </xsd:complexType>
    </xsd:element>
    <xsd:element name="next" maxOccurs="unbounded" minOccurs="0">
    <xsd:complexType>
    <xsd:sequence>
    <xsd:element name="sql" minOccurs="0" maxOccurs="unbounded">
    <xsd:complexType>
    <xsd:attribute name="db"></xsd:attribute>
    </xsd:complexType>
    </xsd:element>
    <xsd:element name="el" minOccurs="0" maxOccurs="unbounded">
    <xsd:complexType>
    <xsd:attribute name="db"></xsd:attribute>
    </xsd:complexType>
    </xsd:element>
    </xsd:sequence>
    </xsd:complexType>
    </xsd:element>
    </xsd:sequence>
    <xsd:attribute name="column" type="xsd:string"></xsd:attribute>
    <xsd:attribute name="auto" type="xsd:boolean"></xsd:attribute>
    <xsd:attribute name="name" type="xsd:string"></xsd:attribute>
    </xsd:complexType>
    </xsd:element>
    <xsd:element name="name" maxOccurs="1" minOccurs="0">
    <xsd:complexType>
    <xsd:sequence>
    <xsd:element name="coldefine" maxOccurs="1" minOccurs="0">
    <xsd:complexType>
    <xsd:attribute name="type" type="xsd:string"></xsd:attribute>
    <xsd:attribute name="width" type="xsd:integer"></xsd:attribute>
    <xsd:attribute name="precision" type="xsd:integer"></xsd:attribute>
    <xsd:attribute name="notNull" type="xsd:boolean"></xsd:attribute>
    <xsd:attribute name="unsigned" type="xsd:boolean"></xsd:attribute>
    <xsd:attribute name="auto" type="xsd:boolean"></xsd:attribute>
    <xsd:attribute name="customType"></xsd:attribute>
    <xsd:attribute name="insert" type="xsd:boolean"></xsd:attribute>
    <xsd:attribute name="update" type="xsd:boolean"></xsd:attribute>
    </xsd:complexType>
    </xsd:element>
    <xsd:element name="prev" maxOccurs="unbounded" minOccurs="0">
    <xsd:complexType>
    <xsd:sequence>
    <xsd:element name="sql" minOccurs="0" maxOccurs="unbounded">
    <xsd:complexType>
    <xsd:attribute name="db"></xsd:attribute>
    </xsd:complexType>
    </xsd:element>
    <xsd:element name="el" minOccurs="0" maxOccurs="unbounded">
    <xsd:complexType>
    <xsd:attribute name="db"></xsd:attribute>
    </xsd:complexType>
    </xsd:element>
    </xsd:sequence>
    </xsd:complexType>
    </xsd:element>
    <xsd:element name="next" maxOccurs="unbounded" minOccurs="0">
    <xsd:complexType>
    <xsd:sequence>
    <xsd:element name="sql" minOccurs="0" maxOccurs="unbounded">
    <xsd:complexType>
    <xsd:attribute name="db"></xsd:attribute>
    </xsd:complexType>
    </xsd:element>
    <xsd:element name="el" minOccurs="0" maxOccurs="unbounded">
    <xsd:complexType>
    <xsd:attribute name="db"></xsd:attribute>
    </xsd:complexType>
    </xsd:element>
    </xsd:sequence>
    </xsd:complexType>
    </xsd:element>
    </xsd:sequence>
    <xsd:attribute name="column" type="xsd:string"></xsd:attribute>
    <xsd:attribute name="name" type="xsd:string"></xsd:attribute>
    </xsd:complexType>
    </xsd:element>
    <xsd:element name="field" maxOccurs="unbounded" minOccurs="0">
    <xsd:complexType>
    <xsd:sequence>
    <xsd:element name="coldefine" maxOccurs="1" minOccurs="0">
    <xsd:complexType>
    <xsd:attribute name="type" type="xsd:string"></xsd:attribute>
    <xsd:attribute name="width" type="xsd:integer"></xsd:attribute>
    <xsd:attribute name="precision" type="xsd:integer"></xsd:attribute>
    <xsd:attribute name="notNull" type="xsd:boolean"></xsd:attribute>
    <xsd:attribute name="unsigned" type="xsd:boolean"></xsd:attribute>
    <xsd:attribute name="auto" type="xsd:boolean"></xsd:attribute>
    <xsd:attribute name="customType"></xsd:attribute>
    <xsd:attribute name="insert" type="xsd:boolean"></xsd:attribute>
    <xsd:attribute name="update" type="xsd:boolean"></xsd:attribute>
    </xsd:complexType>
    </xsd:element>
    <xsd:element name="prev" maxOccurs="unbounded" minOccurs="0">
    <xsd:complexType>
    <xsd:sequence>
    <xsd:element name="sql" minOccurs="0" maxOccurs="unbounded">
    <xsd:complexType>
    <xsd:attribute name="db"></xsd:attribute>
    </xsd:complexType>
    </xsd:element>
    <xsd:element name="el" minOccurs="0" maxOccurs="unbounded">
    <xsd:complexType>
    <xsd:attribute name="db"></xsd:attribute>
    </xsd:complexType>
    </xsd:element>
    </xsd:sequence>
    </xsd:complexType>
    </xsd:element>
    <xsd:element name="next" maxOccurs="unbounded" minOccurs="0">
    <xsd:complexType>
    <xsd:sequence>
    <xsd:element name="sql" minOccurs="0" maxOccurs="unbounded">
    <xsd:complexType>
    <xsd:attribute name="db"></xsd:attribute>
    </xsd:complexType>
    </xsd:element>
    <xsd:element name="el" minOccurs="0" maxOccurs="unbounded">
    <xsd:complexType>
    <xsd:attribute name="db"></xsd:attribute>
    </xsd:complexType>
    </xsd:element>
    </xsd:sequence>
    </xsd:complexType>
    </xsd:element>
    </xsd:sequence>
    <xsd:attribute name="column" type="xsd:string"></xsd:attribute>
    <xsd:attribute name="name" type="xsd:string" use="required"></xsd:attribute>
    </xsd:complexType>
    </xsd:element>
    <xsd:element name="one" maxOccurs="unbounded" minOccurs="0">
    <xsd:complexType>
    <xsd:attribute name="name" type="xsd:string" use="required"></xsd:attribute>
    <xsd:attribute name="target" type="xsd:string" use="required"></xsd:attribute>
    <xsd:attribute name="field" type="xsd:string" use="required"></xsd:attribute>
    <xsd:attribute name="key" type="xsd:string"></xsd:attribute>
    </xsd:complexType>
    </xsd:element>
    <xsd:element name="many" maxOccurs="unbounded" minOccurs="0">
    <xsd:complexType>
    <xsd:attribute name="name" type="xsd:string" use="required"></xsd:attribute>
    <xsd:attribute name="target" type="xsd:string" use="required"></xsd:attribute>
    <xsd:attribute name="field" type="xsd:string" use="required"></xsd:attribute>
    <xsd:attribute name="key" type="xsd:string"></xsd:attribute>
    </xsd:complexType>
    </xsd:element>
    <xsd:element name="manymany" maxOccurs="unbounded" minOccurs="0">
    <xsd:complexType>
    <xsd:attribute name="name" type="xsd:string" use="required"></xsd:attribute>
    <xsd:attribute name="target" type="xsd:string" use="required"></xsd:attribute>
    <xsd:attribute name="relation" type="xsd:string" use="required"></xsd:attribute>
    <xsd:attribute name="from" type="xsd:string" use="required"></xsd:attribute>
    <xsd:attribute name="to" type="xsd:string" use="required"></xsd:attribute>
    <xsd:attribute name="key" type="xsd:string"></xsd:attribute>
    </xsd:complexType>
    </xsd:element>
    <xsd:element name="index" maxOccurs="unbounded" minOccurs="0">
    <xsd:complexType>
    <xsd:attribute name="name" type="xsd:string" use="required"></xsd:attribute>
    <xsd:attribute name="fields" type="xsd:string" use="required"></xsd:attribute>
    <xsd:attribute name="unique" type="xsd:boolean"></xsd:attribute>
    </xsd:complexType>
    </xsd:element>
    </xsd:sequence>
    <xsd:attribute name="type" type="xsd:string" use="required"></xsd:attribute>
    <xsd:attribute name="table" type="xsd:string"></xsd:attribute>
    <xsd:attribute name="view" type="xsd:string"></xsd:attribute>
    <xsd:attribute name="comment" type="xsd:string"></xsd:attribute>
    <xsd:attribute name="pks" type="xsd:string"></xsd:attribute>
    </xsd:complexType>
    </xsd:element>
    </xsd:sequence>
    <xsd:attribute name="package" type="xsd:string"></xsd:attribute>
    </xsd:complexType>
    </xsd:element>

    </xsd:schema>
    15 changes: 15 additions & 0 deletions nutz-dao-test.xml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,15 @@
    <?xml version="1.0" encoding="UTF-8"?>
    <p:nutz-mapping package="org.nutz.dao.impl.entity.xml" xmlns:p="http://nutzam.com/schema/entities" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://nutzam.com/schema/entities nutz-dao-0.1.xsd ">
    <entity comment="" pks="" table="t_iot_user" type="IotUser" view="">
    <id/>
    <name/>
    <field name="age"></field>
    <field column="loc" name="location">
    <coldefine auto="true" customType="" insert="true" notNull="true" precision="0" type="" unsigned="true" update="true" width="0"/>
    </field>
    <one field="level" name="level" target="IotUserLevel"/>
    <many field="userId" name="devices" target=""/>
    <manymany name="devices" from="uid" relation="user_device" target="IotDevice" to="did"/>
    <index fields="age" name="age" unique="true"/>
    </entity>
    </p:nutz-mapping>