/** * Copyright 2009-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.ibatis.mapping; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.type.JdbcType; import org.apache.ibatis.type.TypeHandler; import org.apache.ibatis.type.TypeHandlerRegistry; /** * @author Clinton Begin */ public class ResultMapping { private Configuration configuration; private String property; private String column; private Class<?> javaType; private JdbcType jdbcType; private TypeHandler<?> typeHandler; private String nestedResultMapId; private String nestedQueryId; private Set<String> notNullColumns; private String columnPrefix; private List<ResultFlag> flags; private List<ResultMapping> composites; private String resultSet; private String foreignColumn; private boolean lazy; ResultMapping() { } public static class Builder { private ResultMapping resultMapping = new ResultMapping(); public Builder(Configuration configuration, String property, String column, TypeHandler<?> typeHandler) { this(configuration, property); resultMapping.column = column; resultMapping.typeHandler = typeHandler; } public Builder(Configuration configuration, String property, String column, Class<?> javaType) { this(configuration, property); resultMapping.column = column; resultMapping.javaType = javaType; } public Builder(Configuration configuration, String property) { resultMapping.configuration = configuration; resultMapping.property = property; resultMapping.flags = new ArrayList<ResultFlag>(); resultMapping.composites = new ArrayList<ResultMapping>(); resultMapping.lazy = configuration.isLazyLoadingEnabled(); } public Builder javaType(Class<?> javaType) { resultMapping.javaType = javaType; return this; } public Builder jdbcType(JdbcType jdbcType) { resultMapping.jdbcType = jdbcType; return this; } public Builder nestedResultMapId(String nestedResultMapId) { resultMapping.nestedResultMapId = nestedResultMapId; return this; } public Builder nestedQueryId(String nestedQueryId) { resultMapping.nestedQueryId = nestedQueryId; return this; } public Builder resultSet(String resultSet) { resultMapping.resultSet = resultSet; return this; } public Builder foreignColumn(String foreignColumn) { resultMapping.foreignColumn = foreignColumn; return this; } public Builder notNullColumns(Set<String> notNullColumns) { resultMapping.notNullColumns = notNullColumns; return this; } public Builder columnPrefix(String columnPrefix) { resultMapping.columnPrefix = columnPrefix; return this; } public Builder flags(List<ResultFlag> flags) { resultMapping.flags = flags; return this; } public Builder typeHandler(TypeHandler<?> typeHandler) { resultMapping.typeHandler = typeHandler; return this; } public Builder composites(List<ResultMapping> composites) { resultMapping.composites = composites; return this; } public Builder lazy(boolean lazy) { resultMapping.lazy = lazy; return this; } public ResultMapping build() { // lock down collections resultMapping.flags = Collections.unmodifiableList(resultMapping.flags); resultMapping.composites = Collections.unmodifiableList(resultMapping.composites); resolveTypeHandler(); validate(); return resultMapping; } private void validate() { // Issue #697: cannot define both nestedQueryId and nestedResultMapId if (resultMapping.nestedQueryId != null && resultMapping.nestedResultMapId != null) { throw new IllegalStateException("Cannot define both nestedQueryId and nestedResultMapId in property " + resultMapping.property); } // Issue #5: there should be no mappings without typehandler if (resultMapping.nestedQueryId == null && resultMapping.nestedResultMapId == null && resultMapping.typeHandler == null) { throw new IllegalStateException("No typehandler found for property " + resultMapping.property); } // Issue #4 and GH #39: column is optional only in nested resultmaps but not in the rest if (resultMapping.nestedResultMapId == null && resultMapping.column == null && resultMapping.composites.isEmpty()) { throw new IllegalStateException("Mapping is missing column attribute for property " + resultMapping.property); } if (resultMapping.getResultSet() != null) { int numColumns = 0; if (resultMapping.column != null) { numColumns = resultMapping.column.split(",").length; } int numForeignColumns = 0; if (resultMapping.foreignColumn != null) { numForeignColumns = resultMapping.foreignColumn.split(",").length; } if (numColumns != numForeignColumns) { throw new IllegalStateException("There should be the same number of columns and foreignColumns in property " + resultMapping.property); } } } private void resolveTypeHandler() { if (resultMapping.typeHandler == null && resultMapping.javaType != null) { Configuration configuration = resultMapping.configuration; TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry(); resultMapping.typeHandler = typeHandlerRegistry.getTypeHandler(resultMapping.javaType, resultMapping.jdbcType); } } public Builder column(String column) { resultMapping.column = column; return this; } } public String getProperty() { return property; } public String getColumn() { return column; } public Class<?> getJavaType() { return javaType; } public JdbcType getJdbcType() { return jdbcType; } public TypeHandler<?> getTypeHandler() { return typeHandler; } public String getNestedResultMapId() { return nestedResultMapId; } public String getNestedQueryId() { return nestedQueryId; } public Set<String> getNotNullColumns() { return notNullColumns; } public String getColumnPrefix() { return columnPrefix; } public List<ResultFlag> getFlags() { return flags; } public List<ResultMapping> getComposites() { return composites; } public boolean isCompositeResult() { return this.composites != null && !this.composites.isEmpty(); } public String getResultSet() { return this.resultSet; } public String getForeignColumn() { return foreignColumn; } public void setForeignColumn(String foreignColumn) { this.foreignColumn = foreignColumn; } public boolean isLazy() { return lazy; } public void setLazy(boolean lazy) { this.lazy = lazy; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ResultMapping that = (ResultMapping) o; if (property == null || !property.equals(that.property)) { return false; } return true; } @Override public int hashCode() { if (property != null) { return property.hashCode(); } else if (column != null) { return column.hashCode(); } else { return 0; } } @Override public String toString() { final StringBuilder sb = new StringBuilder("ResultMapping{"); //sb.append("configuration=").append(configuration); // configuration doesn't have a useful .toString() sb.append("property='").append(property).append('\''); sb.append(", column='").append(column).append('\''); sb.append(", javaType=").append(javaType); sb.append(", jdbcType=").append(jdbcType); //sb.append(", typeHandler=").append(typeHandler); // typeHandler also doesn't have a useful .toString() sb.append(", nestedResultMapId='").append(nestedResultMapId).append('\''); sb.append(", nestedQueryId='").append(nestedQueryId).append('\''); sb.append(", notNullColumns=").append(notNullColumns); sb.append(", columnPrefix='").append(columnPrefix).append('\''); sb.append(", flags=").append(flags); sb.append(", composites=").append(composites); sb.append(", resultSet='").append(resultSet).append('\''); sb.append(", foreignColumn='").append(foreignColumn).append('\''); sb.append(", lazy=").append(lazy); sb.append('}'); return sb.toString(); } }