/** * Returns a fixed-size list backed by the specified array. (Changes to * the returned list "write through" to the array.) This method acts * as bridge between array-based and collection-based APIs, in * combination with {@link Collection#toArray}. The returned list is * serializable and implements {@link RandomAccess}. * * <p>This method also provides a convenient way to create a fixed-size * list initialized to contain several elements: * <pre> * List<String> stooges = Arrays.asList("Larry", "Moe", "Curly"); * </pre> * * @param <T> the class of the objects in the array * @param a the array by which the list will be backed * @return a list view of the specified array */ @SafeVarargs @SuppressWarnings("varargs") publicstatic <T> List<T> asList(T... a) { returnnewArrayList<>(a); }
publicvoidadd(int index, E element) { thrownewUnsupportedOperationException(); } public E remove(int index) { thrownewUnsupportedOperationException(); }
privatevoidadd(List list) { // java.lang.ClassCastException: Attempt to insert class java.lang.String element into collection with element type class java.lang.Integer list.add("4"); }
Unmodifiable collections are usually read-only views (wrappers) of other collections. You can’t add, remove or clear them, but the underlying collection can change.
Immutable collections can’t be changed at all - they don’t wrap another collection - they have their own final elements.
/** * Set a parameter to a java.sql.Timestamp value. The driver converts this * to a SQL TIMESTAMP value when it sends it to the database. * * @param parameterIndex * the first parameter is 1, the second is 2, ... * @param x * the parameter value * @param tz * the timezone to use * * @throws SQLException * if a database-access error occurs. */ privatevoidsetTimestampInternal(int parameterIndex, Timestamp x, Calendar targetCalendar, TimeZone tz, boolean rollForward)throws SQLException { ... x = TimeUtil.changeTimezone(this.connection, sessionCalendar, targetCalendar, x, tz, this.connection.getServerTimezoneTZ(), rollForward);
/** * Change the given timestamp from one timezone to another * * @param conn * the current connection to the MySQL server * @param tstamp * the timestamp to change * @param fromTz * the timezone to change from * @param toTz * the timezone to change to * * @return the timestamp changed to the timezone 'toTz' */ publicstatic Timestamp changeTimezone(MySQLConnection conn, Calendar sessionCalendar, Calendar targetCalendar, Timestamp tstamp, TimeZone fromTz, TimeZone toTz, boolean rollForward) { if ((conn != null)) { // 开启 useTimezone=true,才能进入下面的时区转换逻辑 if (conn.getUseTimezone()) { // Convert the timestamp from GMT to the server's timezone CalendarfromCal= Calendar.getInstance(fromTz); fromCal.setTime(tstamp);
LocalDate、LocalTime 是人类易读的日期和时间格式,表示一个本地时间点,基于 ISO 8601 历法系统,无时区信息。
ISO 8601 日期和时间表示法
The ISO 8601 calendar system is the modern civil calendar system used today in most of the world. It is equivalent to the proleptic Gregorian calendar system :
In general, ISO 8601 applies to these representations and formats:
The standard does not assign specific meaning to any element of the dates/times represented: the meaning of any element depends on the context of its use.
Dates and times represented cannot use words that do not have a specified numerical meaning within the standard (thus excluding names of years in the Chinese calendar), or that do not use computer characters (excludes images or sounds).[2]
ISO 8601 的日期和时间表示法为:
In representations that adhere to the ISO 8601 interchange standard :
dates and times are arranged such that the greatest temporal term (typically a year) is placed at the left and each successively lesser term is placed to the right of the previous term.
Representations must be written in a combination of Arabic numerals and the specific computer characters (such as “-“, “:”, “T”, “W”, “Z”) that are assigned specific meanings within the standard; that is, such commonplace descriptors of dates (or parts of dates) as “January”, “Thursday”, or “New Year’s Day” are not allowed in interchange representations within the standard.
publicfinalclassInstant implementsTemporal, TemporalAdjuster, Comparable<Instant>, Serializable { /** * The number of seconds from the epoch of 1970-01-01T00:00:00Z. */ privatefinallong seconds; /** * The number of nanoseconds, later along the time-line, from the seconds field. * This is always positive, and never exceeds 999,999,999. */ privatefinalint nanos; }
publicfinalclassDuration implementsTemporalAmount, Comparable<Duration>, Serializable { /** * The number of seconds in the duration. */ privatefinallong seconds; /** * The number of nanoseconds in the duration, expressed as a fraction of the * number of seconds. This is always positive, and never exceeds 999,999,999. */ privatefinalint nanos; }
Period 的底层实现包含不可变的年、月、日:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
publicfinalclassPeriod implementsChronoPeriod, Serializable { /** * The number of years. */ privatefinalint years; /** * The number of months. */ privatefinalint months; /** * The number of days. */ privatefinalint days; }
Patterns are based on a simple sequence of letters and symbols. A pattern is used to create a Formatter using the ofPattern(String) and ofPattern(String, Locale) methods.
A formatter created from a pattern can be used as many times as necessary, it is immutable and is thread-safe.
HTML Escape Escapes or unescapes an HTML file removing traces of offending characters that could be wrongfully interpreted as markup.
FEATURES Escapes all reserverd characters with their corresponding HTML entities (‘, “, &, <, >) Escapes ISO 8859-1 symbols and characters that have corresponding HTML entities
JSON Escape Escapes or unescapes a JSON string removing traces of offending characters that could prevent parsing.
XML Escape Escapes or unescapes an XML file removing traces of offending characters that could be wrongfully interpreted as markup.
CSV Escape Escapes or unescapes a CSV string removing traces of offending characters that could prevent parsing.
JavaScript Escape Escapes or unescapes a JavaScript string removing traces of offending characters that could prevent interpretation.
Java and .Net Escape Escapes or unescapes a Java or .Net string removing traces of offending characters that could prevent compiling.
SQL Escape Escapes or unescapes a SQL string removing traces of offending characters that could prevent execution.
方式二:代码处理
Apache Commons Lang 提供了字符串转义和反转义工具类 org.apache.commons.lang3.StringEscapeUtils,用于 Java、JavaScript、JSON、HTML、XML、CSV 等字符串:
// 值比较 System.out.println(("a equals b is " + (a.equals(b)))); // a equals b is true
// 引用比较,结果为 false System.out.println("a == b is " + (a == b)); // a == b is false // 引用比较,结果意外的为 true。这是由于 Integer 自动装箱时 [-128, 127] 使用了缓存,详见其 valueOf 方法的源码实现。 System.out.println(("c == d is " + (c == d))); // c == d is true // 引用比较,结果为 false。因为 new Integer(int) 构造方法没有使用缓存。 System.out.println(("e == f is " + (e == f))); // e == f is false
/** * The value of the {@code Byte}. * * @serial */ privatefinalbyte value;
/** * Returns a {@code Byte} instance representing the specified * {@code byte} value. * If a new {@code Byte} instance is not required, this method * should generally be used in preference to the constructor * {@link #Byte(byte)}, as this method is likely to yield * significantly better space and time performance since * all byte values are cached. * * @param b a byte value. * @return a {@code Byte} instance representing {@code b}. * @since 1.5 */ publicstatic Byte valueOf(byte b) { finalintoffset=128; return ByteCache.cache[(int)b + offset]; }
/** * Returns the value of this {@code Byte} as a * {@code byte}. */ publicbytebyteValue() { return value; }
/** * Compares this object to the specified object. The result is * {@code true} if and only if the argument is not * {@code null} and is a {@code Byte} object that * contains the same {@code byte} value as this object. * * @param obj the object to compare with * @return {@code true} if the objects are the same; * {@code false} otherwise. */ publicbooleanequals(Object obj) { if (obj instanceof Byte) { return value == ((Byte)obj).byteValue(); } returnfalse; } }
/** * The value of the {@code Short}. * * @serial */ privatefinalshort value;
/** * Returns a {@code Short} instance representing the specified * {@code short} value. * If a new {@code Short} instance is not required, this method * should generally be used in preference to the constructor * {@link #Short(short)}, as this method is likely to yield * significantly better space and time performance by caching * frequently requested values. * * This method will always cache values in the range -128 to 127, * inclusive, and may cache other values outside of this range. * * @param s a short value. * @return a {@code Short} instance representing {@code s}. * @since 1.5 */ publicstatic Short valueOf(short s) { finalintoffset=128; intsAsInt= s; if (sAsInt >= -128 && sAsInt <= 127) { // must cache return ShortCache.cache[sAsInt + offset]; } returnnewShort(s); }
/** * Returns the value of this {@code Short} as a * {@code short}. */ publicshortshortValue() { return value; }
/** * Compares this object to the specified object. The result is * {@code true} if and only if the argument is not * {@code null} and is a {@code Short} object that * contains the same {@code short} value as this object. * * @param obj the object to compare with * @return {@code true} if the objects are the same; * {@code false} otherwise. */ publicbooleanequals(Object obj) { if (obj instanceof Short) { return value == ((Short)obj).shortValue(); } returnfalse; }
/** * The value of the {@code Integer}. * * @serial */ privatefinalint value;
/** * Returns an {@code Integer} instance representing the specified * {@code int} value. If a new {@code Integer} instance is not * required, this method should generally be used in preference to * the constructor {@link #Integer(int)}, as this method is likely * to yield significantly better space and time performance by * caching frequently requested values. * * This method will always cache values in the range -128 to 127, * inclusive, and may cache other values outside of this range. * * @param i an {@code int} value. * @return an {@code Integer} instance representing {@code i}. * @since 1.5 */ publicstatic Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; returnnewInteger(i); }
/** * Returns the value of this {@code Integer} as an * {@code int}. */ publicintintValue() { return value; }
/** * Compares this object to the specified object. The result is * {@code true} if and only if the argument is not * {@code null} and is an {@code Integer} object that * contains the same {@code int} value as this object. * * @param obj the object to compare with. * @return {@code true} if the objects are the same; * {@code false} otherwise. */ publicbooleanequals(Object obj) { if (obj instanceof Integer) { return value == ((Integer)obj).intValue(); } returnfalse; }
/** * The value of the {@code Long}. * * @serial */ privatefinallong value;
/** * Returns a {@code Long} instance representing the specified * {@code long} value. * If a new {@code Long} instance is not required, this method * should generally be used in preference to the constructor * {@link #Long(long)}, as this method is likely to yield * significantly better space and time performance by caching * frequently requested values. * * Note that unlike the {@linkplain valueOf(int) * corresponding method} in the {@code Integer} class, this method * is <em>not</em> required to cache values within a particular * range. * * @param l a long value. * @return a {@code Long} instance representing {@code l}. * @since 1.5 */ publicstatic Long valueOf(long l) { finalintoffset=128; if (l >= -128 && l <= 127) { // will cache return LongCache.cache[(int)l + offset]; } returnnewLong(l); }
/** * Returns the value of this {@code Long} as a * {@code long} value. */ publiclonglongValue() { return value; }
/** * Compares this object to the specified object. The result is * {@code true} if and only if the argument is not * {@code null} and is a {@code Long} object that * contains the same {@code long} value as this object. * * @param obj the object to compare with. * @return {@code true} if the objects are the same; * {@code false} otherwise. */ publicbooleanequals(Object obj) { if (obj instanceof Long) { return value == ((Long)obj).longValue(); } returnfalse; }
/** * The value of the Float. * * @serial */ privatefinalfloat value;
/** * Returns a {@code Float} instance representing the specified * {@code float} value. * If a new {@code Float} instance is not required, this method * should generally be used in preference to the constructor * {@link #Float(float)}, as this method is likely to yield * significantly better space and time performance by caching * frequently requested values. * * @param f a float value. * @return a {@code Float} instance representing {@code f}. * @since 1.5 */ publicstatic Float valueOf(float f) { returnnewFloat(f); }
/** * Compares this object against the specified object. The result * is {@code true} if and only if the argument is not * {@code null} and is a {@code Float} object that * represents a {@code float} with the same value as the * {@code float} represented by this object. For this * purpose, two {@code float} values are considered to be the * same if and only if the method {@link #floatToIntBits(float)} * returns the identical {@code int} value when applied to * each. * * <p>Note that in most cases, for two instances of class * {@code Float}, {@code f1} and {@code f2}, the value * of {@code f1.equals(f2)} is {@code true} if and only if * * <blockquote><pre> * f1.floatValue() == f2.floatValue() * </pre></blockquote> * * <p>also has the value {@code true}. However, there are two exceptions: * <ul> * <li>If {@code f1} and {@code f2} both represent * {@code Float.NaN}, then the {@code equals} method returns * {@code true}, even though {@code Float.NaN==Float.NaN} * has the value {@code false}. * <li>If {@code f1} represents {@code +0.0f} while * {@code f2} represents {@code -0.0f}, or vice * versa, the {@code equal} test has the value * {@code false}, even though {@code 0.0f==-0.0f} * has the value {@code true}. * </ul> * * This definition allows hash tables to operate properly. * * @param obj the object to be compared * @return {@code true} if the objects are the same; * {@code false} otherwise. * @see java.lang.floatToIntBits(float) */ publicbooleanequals(Object obj) { return (obj instanceof Float) && (floatToIntBits(((Float)obj).value) == floatToIntBits(value)); }
/** * The value of the Double. * * @serial */ privatefinaldouble value;
/** * Returns a {@code Double} instance representing the specified * {@code double} value. * If a new {@code Double} instance is not required, this method * should generally be used in preference to the constructor * {@link #Double(double)}, as this method is likely to yield * significantly better space and time performance by caching * frequently requested values. * * @param d a double value. * @return a {@code Double} instance representing {@code d}. * @since 1.5 */ publicstatic Double valueOf(double d) { returnnewDouble(d); }
/** * Compares this object against the specified object. The result * is {@code true} if and only if the argument is not * {@code null} and is a {@code Double} object that * represents a {@code double} that has the same value as the * {@code double} represented by this object. For this * purpose, two {@code double} values are considered to be * the same if and only if the method {@link * #doubleToLongBits(double)} returns the identical * {@code long} value when applied to each. * * <p>Note that in most cases, for two instances of class * {@code Double}, {@code d1} and {@code d2}, the * value of {@code d1.equals(d2)} is {@code true} if and * only if * * <blockquote> * {@code d1.doubleValue() == d2.doubleValue()} * </blockquote> * * <p>also has the value {@code true}. However, there are two * exceptions: * <ul> * <li>If {@code d1} and {@code d2} both represent * {@code Double.NaN}, then the {@code equals} method * returns {@code true}, even though * {@code Double.NaN==Double.NaN} has the value * {@code false}. * <li>If {@code d1} represents {@code +0.0} while * {@code d2} represents {@code -0.0}, or vice versa, * the {@code equal} test has the value {@code false}, * even though {@code +0.0==-0.0} has the value {@code true}. * </ul> * This definition allows hash tables to operate properly. * @param obj the object to compare with. * @return {@code true} if the objects are the same; * {@code false} otherwise. * @see java.lang.Double#doubleToLongBits(double) */ publicbooleanequals(Object obj) { return (obj instanceof Double) && (doubleToLongBits(((Double)obj).value) == doubleToLongBits(value)); }
/** * The value of the Boolean. * * @serial */ privatefinalboolean value;
/** * Returns a {@code Boolean} instance representing the specified * {@code boolean} value. If the specified {@code boolean} value * is {@code true}, this method returns {@code Boolean.TRUE}; * if it is {@code false}, this method returns {@code Boolean.FALSE}. * If a new {@code Boolean} instance is not required, this method * should generally be used in preference to the constructor * {@link #Boolean(boolean)}, as this method is likely to yield * significantly better space and time performance. * * @param b a boolean value. * @return a {@code Boolean} instance representing {@code b}. * @since 1.4 */ publicstatic Boolean valueOf(boolean b) { return (b ? TRUE : FALSE); }
/** * Returns the value of this {@code Boolean} object as a boolean * primitive. * * @return the primitive {@code boolean} value of this object. */ publicbooleanbooleanValue() { return value; }
/** * Returns {@code true} if and only if the argument is not * {@code null} and is a {@code Boolean} object that * represents the same {@code boolean} value as this object. * * @param obj the object to compare with. * @return {@code true} if the Boolean objects represent the * same value; {@code false} otherwise. */ publicbooleanequals(Object obj) { if (obj instanceof Boolean) { return value == ((Boolean)obj).booleanValue(); } returnfalse; }
/** * The value of the {@code Character}. * * @serial */ privatefinalchar value;
/** * Returns a <tt>Character</tt> instance representing the specified * <tt>char</tt> value. * If a new <tt>Character</tt> instance is not required, this method * should generally be used in preference to the constructor * {@link #Character(char)}, as this method is likely to yield * significantly better space and time performance by caching * frequently requested values. * * This method will always cache values in the range {@code * '\u005Cu0000'} to {@code '\u005Cu007F'}, inclusive, and may * cache other values outside of this range. * * @param c a char value. * @return a <tt>Character</tt> instance representing <tt>c</tt>. * @since 1.5 */ publicstatic Character valueOf(char c) { if (c <= 127) { // must cache return CharacterCache.cache[(int)c]; } returnnewCharacter(c); }
/** * Returns the value of this {@code Character} object. * @return the primitive {@code char} value represented by * this object. */ publiccharcharValue() { return value; }
/** * Compares this object against the specified object. * The result is {@code true} if and only if the argument is not * {@code null} and is a {@code Character} object that * represents the same {@code char} value as this object. * * @param obj the object to compare with. * @return {@code true} if the objects are the same; * {@code false} otherwise. */ publicbooleanequals(Object obj) { if (obj instanceof Character) { return value == ((Character)obj).charValue(); } returnfalse; }
If you are inserting many rows from the same client at the same time, use INSERT statements with multiple VALUES lists to insert several rows at a time. This is considerably faster (many times faster in some cases) than using separate single-row INSERT statements.
Whenever MyBatis sets a parameter on a PreparedStatement or retrieves a value from a ResultSet, a TypeHandler is used to retrieve the value in a means appropriate to the Java type.
When you are dealing with a constructor with many parameters, maintaining the order of arg elements is error-prone.
Since 3.4.3, by specifying the name of each parameter, you can write arg elements in any order. To reference constructor parameters by their names, you can
either add @Param annotation to them
or compile the project with ‘-parameters‘ compiler option and enable useActualParamName (this option is enabled by default).
javaType can be omitted if there is a property with the same name and type.
注意,如果抛出异常如下:
1
org.apache.ibatis.executor.ExecutorException: No constructor found in ...
解决方案:
如果是可变类(mutable classes),为类添加无参构造方法。
如果是不可变类(immutable classes),Mapper XML 结果映射 resultMap 必须使用正确参数的 constructor。
嵌套结果映射
两种嵌套关系配置:
association “has-one” type relationship
collection “has many” type relationship
两种查询方式:
嵌套查询(Nested Select),即分开多次查询。
嵌套结果映射(Nested Results),即利用表连接语法进行一次性的连表查询;
The association element deals with a “has-one” type relationship. For example, in our example, a Blog has one Author. An association mapping works mostly like any other result. You specify the target property, the javaType of the property (which MyBatis can figure out most of the time), the jdbcType if necessary and a typeHandler if you want to override the retrieval of the result values.
Where the association differs is that you need to tell MyBatis how to load the association. MyBatis can do so in two different ways:
Nested Select: By executing another mapped SQL statement that returns the complex type desired.
Nested Results: By using nested result mappings to deal with repeating subsets of joined results.
总结:
嵌套查询(Nested Select)
嵌套结果映射(Nested Results)
association 多对一关联
禁用
可用
collection 一对多关联
可用
可用
association 多对一关联,嵌套查询(Nested Select)存在 N+1 次查询的性能问题,不建议使用。
While this approach Nested Select is simple, it will not perform well for large data sets or lists. This problem is known as the “N+1 Selects Problem”. In a nutshell, the N+1 selects problem is caused like this:
You execute a single SQL statement to retrieve a list of records (the “+1”).
For each record returned, you execute a select statement to load details for each (the “N”).
This problem could result in hundreds or thousands of SQL statements to be executed. This is not always desirable.
The upside is that MyBatis can lazy load such queries, thus you might be spared the cost of these statements all at once. However, if you load such a list and then immediately iterate through it to access the nested data, you will invoke all of the lazy loads, and thus performance could be very bad.
<!-- 使用表连接嵌套查询学生、学校 --> <selectid="listStudents"resultMap="ExtendResultMap"> SELECT <includerefid="Extend_column_list" /> FROM t_student T INNER JOIN t_school E ON T.school_no = E.school_no WHERE T.age = #{age} </select>
SchoolMapper.xml
1 2 3 4 5 6 7 8 9 10 11 12 13
<sqlid="Base_Column_List"> E.id AS school_id, ...... E.create_time AS school_create_time, E.update_time AS school_update_time </sql>
MyBatis 回写主键利用了 JDBC 的特性,适用于支持自动生成主键的数据库,比如 MySQL 和 SQL Server。
First, if your database supports auto-generated key fields (e.g. MySQL and SQL Server), then you can simply set useGeneratedKeys="true" and set the keyProperty to the target property and you’re done.
1 2 3 4
<insertid="insertAuthor"useGeneratedKeys="true"keyProperty="id"> insert into Author (username,password,email,bio) values (#{username},#{password},#{email},#{bio}) </insert>
keyProperty attribute:
(insert and update only) Identifies a property into which MyBatis will set the key value returned by getGeneratedKeys, or by a selectKey child element of the insert statement. Default: unset.
自定义生成主键
适用于不支持自动生成主键的数据库,比如 Oracle。
MyBatis has another way to deal with key generation for databases that don’t support auto-generated column types, or perhaps don’t yet support the JDBC driver support for auto-generated keys.
Here’s a simple (silly) example that would generate a random ID (something you’d likely never do, but this demonstrates the flexibility and how MyBatis really doesn’t mind).
The selectKey statement would be run first, the Author id property would be set, and then the insert statement would be called. This gives you a similar behavior to an auto-generated key in your database without complicating your Java code.
1 2 3 4 5 6 7 8 9
<insertid="insertAuthor"> <selectKeykeyProperty="id"resultType="int"order="BEFORE"> select CAST(RANDOM()*1000000 as INTEGER) a from SYSIBM.SYSDUMMY1 </selectKey> insert into Author (id, username, password, email,bio, favourite_section) values (#{id}, #{username}, #{password}, #{email}, #{bio}, #{favouriteSection,jdbcType=VARCHAR}) </insert>