MyBatis 动态SQL(十二)
动态条件查询
以下是我们数据库表 tb_user 的记录:
假设现在有一个需求,就是根据输入的用户年龄和性别,查询用户的记录信息。你可能会说,这太简单了,脑袋里立马蹦出如下的 SQL 语句:
SELECT * FROM `tb_user` where age = 21 and sex = 1
你可能会觉得这条 SQL 语句还不够完美,因为用户名和年龄是输入的参数,不是写死,应该用占位符替换一下,所以修改如下:
SELECT * FROM `tb_user` where age = ? and sex = ?
你可能认为一切到此结束了,但是你想过没有还有以下这些情况?
- 输入的年龄和性别都是 null
- 输入的性别有值,但年龄为 null
- 输入的年龄有值,但性别 null
- 输入的年龄和性别都有值
现在明白了吧,你其实只处理了以上四种情况中的一种,具体而言是最后一种,还有三种情况并没有处理。
你可能觉得,我看不出剩下的三种情况与已经处理的最后那种情况有什么区别。
好吧,我们一起看看吧,我把 SQL 语句改为第一种情况,也就是年龄和性别都是 null,如下:
SELECT * FROM `tb_user` where age = null and sex = null
执行结果如下:
这个结果并不是我们想要的,因为当输入的年龄和性别参数为空,正确结果应该是查到所有 user 记录才对。
所以,正确的 SQL 语句应该如下:
SELECT * FROM `tb_user`
这四种情况的处理,一条 SQL 语句是搞不定的,应该要用四条 SQL 语句。
SELECT * FROM `tb_user`
SELECT * FROM `tb_user` where sex = ?
SELECT * FROM `tb_user` where age = ?
SELECT * FROM `tb_user` where age = ? and sex = ?
以上情况就是所谓动态条件查询,也就是当查询条件动态改变时,不同的查询条件对应不同的 SQL 语句。
之前我们动态查询条件是年龄和性别,那么如果我再增加一个查询条件,比如姓名,这又会有多少种情况呀?
相信你很快就有答案了,是八种,没错吧。
怎么得来的呀,这很简单,就是数学的排列组合。当两个动态查询条件,是四种处理情况,也就是2的平方;当三个动态查询条件,就是2的三次方,有八种;以此类推,当有四个动态查询条件,那么就是2的四次方,有十六种。
你可能会想,如果有四种动态查询条件,我得写十六条 SQL 语句,这也太夸张了吧。
那有没有什么办法,无论有多少个动态查询条件,都只需要写一条 SQL 语句。
答案是,有,当然有啦,那就是 MyBatis 动态 SQL。
MyBatis 动态 SQL
动态 SQL 是 MyBatis 的一个强大的特性之一,它提供了 OGNL 表达式动态生成 SQL 的功能。
if
if 语句用来解决动态条件查询问题,它可以实现根据条件拼接 SQL 语句,也就是一条 SQL 语句搞定动态条件查询哈。
我们还是用之前年龄和性别两个动态条件查询,来说一说 if 语句的用法。
Mapper 接口方法:
public List<UserEntity> selectUserByAgeAndSex(@Param("userOne") UserEntity userOne,@Param("userTwo") UserEntity userTwo);
SQL 语句映射:
<select id="selectUserByAgeAndSex" resultMap="userResultMap">
select * from tb_user where age > #{userOne.age} and sex = #{userTwo.sex};
</select>
SQL 语句映射(增加动态条件查询):
<select id="selectUserByAgeAndSex" resultMap="userResultMap">
select * from tb_user where 1=1
<if test="userOne != null">
and age > #{userOne.age}
</if>
<if test="userTwo != null">
and sex = #{userTwo.sex}
</if>
</select>
接下来,我们分别测试动态条件查询的四种情况,测试前记得要打开 log4j 的 debug 日志开关,这样才能看到 MyBatis 在调试日志中生成的 SQL 语句。
log4j.rootLogger=DEBUG
现在执行之前写的 JUnit 测试方法,如下:
@Test
public void selectUserByAgeAndSexTest() {
UserEntity userEntityOne = new UserEntity();
userEntityOne.setAge(20);
UserEntity userEntityTwo = new UserEntity();
userEntityTwo.setSex(1); List<UserEntity> userEntitie
= userMapper.selectUserByAgeAndSex(userEntityOne,userEntityTwo);
System.out.println(userEntities);
Assert.assertNotNull(userEntities);
}
以上执行结果和以前相同,是四种情况中最后一种,生成的 SQL 语句如下:
2020-07-06 21:25:54,791 [main] [mapper.UserMapper.selectUserByAgeAndSex]-[DEBUG] ==> Preparing: select * from tb_user where 1=1 and age > ? and sex = ?
2020-07-06 21:25:54,836 [main] [mapper.UserMapper.selectUserByAgeAndSex]-[DEBUG] ==> Parameters: 20(Integer), 1(Integer)
我把查询条件修改一下,如下:
userMapper.selectUserByAgeAndSex(null,null);
再执行测试,生成的 SQL 语句如下:
2020-07-06 21:28:06,789 [main] [mapper.UserMapper.selectUserByAgeAndSex]-[DEBUG] ==> Preparing: select * from tb_user where 1=1
2020-07-06 21:28:06,898 [main] [mapper.UserMapper.selectUserByAgeAndSex]-[DEBUG] ==> Parameters:
相信看到这里,你应该大致明白 if 语句的作用了吧。还有两种情况,我们继续修改查询条件,如下:
userMapper.selectUserByAgeAndSex(userEntityOne,null);
再执行测试,生成的 SQL 语句如下:
2020-07-06 21:30:46,695 [main] [mapper.UserMapper.selectUserByAgeAndSex]-[DEBUG] ==> Preparing: select * from tb_user where 1=1 and age > ?
2020-07-06 21:30:46,758 [main] [mapper.UserMapper.selectUserByAgeAndSex]-[DEBUG] ==> Parameters: 20(Integer)
最后一种情况了,修改查询条件,如下:
userMapper.selectUserByAgeAndSex(null,userEntityTwo);
执行测试,生成的 SQL 语句如下:
2020-07-06 21:31:59,133 [main] [mapper.UserMapper.selectUserByAgeAndSex]-[DEBUG] ==> Preparing: select * from tb_user where 1=1 and sex = ?
2020-07-06 21:31:59,195 [main] [mapper.UserMapper.selectUserByAgeAndSex]-[DEBUG] ==> Parameters: 1(Integer)
if 语句的作用好比 Java 中的 if 语句,它根据 test 判断条件如果为 true 则拼接里面包含的 SQL 语句片段,如果为 false 则不拼接 SQL 语句片段,语法如下:
<if test="判断条件">
拼接的SQL语句片段
</if>
这里要注意一下,test 的判断条件直接是参数名,而不需要加 #{} 或者 ${}。
以后遇到动态条件查询,有多少个查询条件就写多少个 if 语句即可,是不是很方便呀。
where
可能有同学还会有一个疑惑,就是为什么要在 where 后加上 1=1 ,感觉怪怪的。其实,很容易想明白,目的是为了拼接 SQL 语句时语法正确。如果不加的话,可能会出现如下 SQL 语句:
select * from tb_user where and sex = ? #语法错误
这条 SQL 语句明显语法错误,现在应该明白加上 1=1 的用处了吧。
可能你还不死心,觉得这种写法看起来有点别扭,有没有办法不加 1=1 呀?
呵呵,还真有办法,MyBatis 开发者也考虑到这一点,所以专门提供了一个 where 语句,就是拿来搞定这个的。
用法如下:
<select id="selectUserByAgeAndSex" resultMap="userResultMap">
select * from tb_user
<where>
<if test="userOne != null">
and age > #{userOne.age}
</if>
<if test="userTwo != null">
and sex = #{userTwo.sex}
</if>
</where>
</select>
可以看到,where 语句用于格式化输出,并没有什么实质的作用,只是让 SQL 语句看起来舒服一点罢了。
choose / when / otherwise
choose / when / otherwise 语句其实和 if 语句作用差不多,但是也有一些区别。
我们还是用之前年龄和性别两个动态条件查询,来说一说它的用法。
<select id="selectUserByAgeAndSex" resultMap="userResultMap">
select * from tb_user
<where>
<choose>
<when test="userOne != null">
age > #{userOne.age}
</when>
<when test="userTwo != null">
sex = #{userTwo.sex}
</when>
<otherwise>
age > 20 and sex = 1
</otherwise>
</choose>
</where>
</select>这里的 choose / when / otherwise 语句的作用其实和 Java 里的 switch / case / default 语句或者 if / elseif / else 语句差不多。
它和之前的 if 语句的区别,在于一个 choose 语句可以有多个条件判断分支,每一个 when 语句代表一个条件判断分支。当有一个 when 语句满足条件,其他的 when 语句不再执行条件判断,当所有的 when 都不满足条件,那么就选择默认分支 otherwise。
说了半天,我们还是测试一下,看看效果如何,测试代码如下:
List<UserEntity> userEntities
= userMapper.selectUserByAgeAndSex(null,null);我现在把两个条件查询参数都设置为 null,那么两个 when 语句都不满足条件,最终流程应该选择 otherwise 分支。
执行结果如下:
2020-07-07 16:44:18,077 [main] [mapper.UserMapper.selectUserByAgeAndSex]-[DEBUG] ==> Preparing: select * from tb_user WHERE age > 20 and sex = 1
结果果然不出所料。
现在 if 语句和 choose 语句的使用和区别都明白了, 那么在实际项目开发中该用哪个呢?
如果在 Java 里你知道何时用 if 语句或 switch / case / default 语句,我相信何时使用 if 语句或 choose / when / otherwise 语句,对你来说绝不是问题。
foreach
看到 foreach 语句很容易联想到 Java 里 for 语句的增强版 foreach 语句,用法如下:
//创建List并添加元素
List<String> list = new ArrayList<String>();
list.add("1");
list.add("3");
list.add("4"); //利用froeach语句输出集合元素
for (String x : list) {
System.out.println(x);
}MyBatis 提供的 foreach 语句主要用来对一个集合进行遍历,通常是用来构建 IN 条件语句,也可用于其他情况下动态拼接 SQL 语句。
我们还是通过一个例子讲解 foreach 语句如何使用的,不过我要事先申明有一定难度哈。
假设我有一个需求,就是我的输入参数是姓名集合(里面有一堆姓名,如张三、李四、王五),需要从数据库中依次查询出对应的用户信息。
看到这个需求,如果你的 SQL 还算扎实,应该立马想到 IN 条件语句,而且脑袋里立刻浮现出如下 SQL 语句:
select * from tb_user where name in ('张三','李四','王五')
执行结果如下:
如果你脑袋里一片空白,建议抽空复习一下 SQL。
以上是直接在数据库里写 SQL 语句,要记住需求里的姓名集合是通过输入参数传递进来的,而不是这里直接写死的哈。
首先,我们需要在 UserMapper.java 里增加一个接口方法,而且方法的参数是姓名集合,如下:
/**
* 根据姓名集合查询用户信息
* @param names 姓名集合
* @return 用户实体集合
*/
public List<UserEntity> selectUserByNameList(List<String> names);接着,在 UserMapper.xml 里增加这个接口方法的 SQL 语句映射,如下:
<select id="selectUserByNameList" resultMap="userResultMap">
select * from tb_user where
<foreach item="name" collection="list" index="index"
open="name in (" separator="," close=")">
#{name}
</foreach>
</select>以上可知,foreach 语句有几个属性,如下:
collection:表示需要遍历的集合,它的属性值有三种情况,如下:
如果传入的是单参数且参数类型是一个 List 的时候,collection 属性值为 list
如果传入的是单参数且参数类型是一个 Array 数组的时候,collection 的属性值为 array
如果传入的参数是多个的时候,一般需要使用 @param 取别名,collection 属性值为别名
item:表示每次遍历时生成的对象名
index:表示在迭代过程中,每次迭代到的位置
open:表示开始遍历时要拼接的字符串
separator:表示在每次遍历时两个对象直接的连接字符串
close:表示结束遍历时要拼接的字符串
看到 foreach 语句居然有这么多属性,是不是觉得掌握有点难度,这只是纸老虎而已,别被它吓住了。
你只需要明白一点,以上 foreach 语句意思无非就是从 List 集合中迭代获取每个元素,然后再进行 SQL语句拼接,工作原理类似于 Java 的 for 循环动态拼接字符串,如下:
String sql = "";
sql += "name in ("; // 类似 open 属性(前缀)
for(int i = 0;i < list.size;i++){//list 类似 collection 集合属性 ,i 类似 index 属性(集合下标)
sql += list[i]; // 相当于 #{name},而 name 和 item 属性值相同
sql += ","; // 类似 separator 属性(分隔符)
}
sql += ")" // 类似 close 属性作用(后缀)
System.out.println(sql); //打印结果为 name in ("张三","李四","王五")这几个属性中,其他属性值照着填写即可,collection 属性值不能乱填,它有三种情况,规则上面已经写得很清楚了,由于我们只有一个参数而且集合是 List 类型,所以适合情况一,collection 属性值应该填写为 list。
最后,在 MyBatisTest.java 中添加单元测试方法,如下:
@Test
public void selectUserByNameListTest() {
ArrayList<String> names = new ArrayList<String>();
names.add("张三");
names.add("李四");
names.add("王五"); List<UserEntity> userEntities = userMapper.selectUserByNameList(names);
System.out.println(userEntities); Assert.assertNotNull(userEntities);
}执行测试,结果如下:
2020-07-08 11:39:28,659 [main] [mapper.UserMapper.selectUserByNameList]-[DEBUG] ==> Preparing: select * from tb_user where name in ( ? , ? , ? )
2020-07-08 11:39:28,705 [main] [mapper.UserMapper.selectUserByNameList]-[DEBUG] ==> Parameters: 张三(String), 李四(String), 王五(String)
2020-07-08 11:39:28,752 [main] [mapper.UserMapper.selectUserByNameList]-[DEBUG] <== Total: 3
[UserEntity{id=1, userName='zs', password='123456', name='张三', age=22, sex=1, birthday=Sun Sep 02 00:00:00 IRKST 1990, created='2020-06-17 09:30:58.0', updated='2020-06-17 09:30:58.0', interests=null},
UserEntity{id=2, userName='ls', password='123456', name='李四', age=24, sex=1, birthday=Sun Sep 05 00:00:00 IRKST 1993, created='2020-06-17 09:30:58.0', updated='2020-06-17 09:30:58.0', interests=null},
UserEntity{id=6, userName='ww', password='123456', name='王五', age=21, sex=1, birthday=Fri Jan 10 00:00:00 IRKT 1992, created='2020-06-24 18:53:48.0', updated='2020-06-24 18:53:48.0', interests=null}]foreach 语句最终迭代拼接 SQL 语句构成了一条 IN 条件语句,输入参数 List 集合的元素分别对应三个参数占位符。
我们再仔细想一想,有没有发现这里 foreach 语句的作用其实就是批量查询,等价于三条 SQL 语句如下:
select * from tb_user where name in ( ? , ? , ? ) # 上面一条 SQL 语句等价于下面三条 SQL 语句
select * from tb_user where name = ? #占位符是张三
select * from tb_user where name = ? #占位符是李四
select * from tb_user where name = ? #占位符是王五那么 foreach 可以完成批量查询,那么也可以完成批量删除等。
set
set 语句用于更新操作,功能和 where 语句差不多。
假设我们有一个需求,需要更新某个指定用户的姓名和用户名,但是只有姓名不为 null 才更新
映射接口方法:
/**
* 更新用户姓名
* @param user 用户姓名
* @return 影响行数
*/
public int updateUser(@Param("id") int id,@Param("name") String name);SQL 语句映射:
<update id="updateUser">
update tb_user set
<if test="name != null">
name=#{name},
</if>
<if test="name != null">
user_name=#{name},
</if>
where id=#{id};
</update>我们使用 if 语句就可以轻松搞定,而且用一个 if 就够了,但是我用了两个 if 语句,目的是为了讲解 set 语句的作用。
单元测试代码如下:
@Test
public void updateUserTest() {
int result = userMapper.updateUser(1,"张三三");
sqlSession.commit();
Assert.assertEquals(1,result);
}执行测试,结果如下:
SQL: update tb_user set name=?, user_name=?, where id=?;
### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'where id=1' at line 8报语法错误,很明显,执行的 SQL 语句语法不对,user_name 后面多了一个逗号。
怎么改?你会觉得这还不简单,把多余的逗号去掉就可以啦。
但是,你想一想如果只有第一个 if 语句满足条件,那么一样也会出问题,SQL 语句变为如下:
update tb_user set name=?, where id=?; #SQL 语法错误
或者 两个 if 语句都不满足条件,那么 SQL语句变为如下:
update tb_user set where id=?; #SQL 语法错误
结果都会报 SQL 语法错误问题。
看来这个问题还有点棘手,这时候就该我们的 set 语句出场啦,如下:
<update id="updateUser">
update tb_user
<set>
<if test="name != null">
name=#{name},
</if>
<if test="name != null">
user_name=#{name},
</if>
</set>
where id=#{id};
</update>set 语句的作用就是更新操作时自动删除多余的逗号
当然,还有一个问题 set 语句仍然无法解决,就是如果两个 if 语句都不满足条件,即 set 语句后面为空,如下:
update tb_user set where id=?; #SQL 语法错误
trim
trim 元素的主要有两个功能:
可以在自己包含的内容前加上某些前缀,也可以在其后加上某些后缀
- prefix 属性(添加前缀)
- suffix 属性(添加后缀)
可以把包含内容的首部某些内容覆盖,即忽略,也可以把尾部的某些内容覆盖,
prefixOverrides 属性(覆盖首部)
suffixOverrides 属性(覆盖尾部)
正因为 trim 语句有这样的功能,trim 语句可以用来实现 where 语句和 set 语句的效果。
- trim 语句实现 where 语句效果如下:
<!-- where 语句 -->
<select id="selectUserByAgeAndSex" resultMap="userResultMap">
select * from tb_user
<where>
<if test="userOne != null">
age > #{userOne.age}
</if>
<if test="userTwo != null">
sex = #{userTwo.sex}
</if>
</where>
</select> <!-- trim 语句实现 where 语句-->
<select id="selectUserByAgeAndSex" resultMap="userResultMap">
select * from tb_user
<trim prefix="where" prefixOverrides="and | or">
<if test="userOne != null">
and age > #{userOne.age}
</if>
<if test="userTwo != null">
and sex = #{userTwo.sex}
</if>
</trim>
</select>prefix="where" 表示在 trim 语句包含的语句前添加前缀 where
prefixOverrides="and | or" 表示在 trim 语句包含的语句前出现 and 或 or 则自动忽略
想一想,这不就是之前 where 语句的功能,trim 语句确实可以代替 where 语句。
trim 语句实现 set 语句效果如下:
<!-- set 语句 -->
<update id="updateUser">
update tb_user
<set>
<if test="name != null">
name=#{name},
</if>
<if test="name != null">
user_name=#{name},
</if>
</set>
where id=#{id};
</update> <!-- trim 语句实现 set语句 -->
<update id="updateUser">
update tb_user
<trim prefix="set" suffixOverrides=",">
<if test="name != null">
name=#{name},
</if>
<if test="name != null">
user_name=#{name},
</if>
</trim>
where id=#{id};
</update>prefix="set" 表示在 trim 语句包含的语句前添加前缀 setsuffixOverrides="," 表示在 trim 语句包含的语句后出现逗号则自动忽略
想一想,这不就是之前 set 语句的功能,trim 语句确实可以代替 set 语句。
以上可知,trim 语句比 set 语句和 where 语句更加灵活,但是使用也更复杂。 它不仅可以实现 set 语句和 where 语句的功能,还可以实现更多内容处理。
实际项目开发中,能用 set 语句或 where 语句尽量不用 trim 语句,可以理解 trim 语句是一个更加底层的内容处理语句。
总结
所有动态 SQL 语句本质都是简单的对 SQL 语句进行拼接、处理和优化
注意事项:
尽管动态 SQL 可以灵活的拼接 SQL 语句,但是也不要滥用 动态 SQL,尽可能业务逻辑比较相似的,通过条件进行控制。试想一下如果一整张表的所有逻辑全都是一条 SQL 语句,通过各种 if 或者 choose 拼接起来,这并不能代表你很牛逼,只能代表你很傻逼,因为可读性和可维护性非常差,出了问题排查起来就会要你的命
对于动态 SQL 根本仍旧是 SQL 的编写,所以需要具有良好的 SQL 语句编写能力,动态 SQL 只是可以让 SQL 语句更加灵活,并不能解决你 SQL 语句中的任何问题或者性能问题
MyBatis 动态SQL(十二)的更多相关文章
- MyBatis动态SQL(二)
1.foreach foreach语句用来遍历数组和集合对象.标签中的属性: collection属性:值有三种list.array.map open属性:表示调用的sql语句前缀添加的内容 clos ...
- MyBatis动态SQL之一使用 if 标签和 choose标签
bootstrap react https://segmentfault.com/a/1190000010383464 xml 中 < 转义 to thi tha <if test=&qu ...
- Mybatis动态sql及分页、特殊符号
目的: mybatis动态sql(案例:万能查询) 查询返回结果集的处理 mybatis的分页运用 mybatis的特殊符号 mybatis动态sql(案例:万能查询) 根据id查询 模糊查询 (参数 ...
- Mybatis 动态sql(转载)
原文地址:http://www.cnblogs.com/dongying/p/4092662.html 传统的使用JDBC的方法,相信大家在组合复杂的的SQL语句的时候,需要去拼接,稍不注意哪怕少了个 ...
- mybatis实战教程(mybatis in action)之八:mybatis 动态sql语句
mybatis 的动态sql语句是基于OGNL表达式的.可以方便的在 sql 语句中实现某些逻辑. 总体说来mybatis 动态SQL 语句主要有以下几类:1. if 语句 (简单的条件判断)2. c ...
- 9.mybatis动态SQL标签的用法
mybatis动态SQL标签的用法 动态 SQL MyBatis 的强大特性之一便是它的动态 SQL.如果你有使用 JDBC 或其他类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句有多么 ...
- 自己动手实现mybatis动态sql
发现要坚持写博客真的是一件很困难的事情,各种原因都会导致顾不上博客.本来打算写自己动手实现orm,看看时间,还是先实现一个动态sql,下次有时间再补上orm完整的实现吧. 用过mybatis的人,估计 ...
- Mybatis动态SQL单一基础类型参数用if标签
Mybatis动态SQL单一基础类型参数用if标签时,test中应该用 _parameter,如: 1 2 3 4 5 6 <select id="selectByName" ...
- 超全MyBatis动态SQL详解!( 看完SQL爽多了)
MyBatis 令人喜欢的一大特性就是动态 SQL. 在使用 JDBC 的过程中, 根据条件进行 SQL 的拼接是很麻烦且很容易出错的. MyBatis 动态 SQL 的出现, 解决了这个麻烦. My ...
随机推荐
- Redhat 安装gitlab
以下为8月23日左右记录的,没有发布.今日整理大概记录下. 安装依赖包 yum install -y curl policycoreutils-python openssh-server openss ...
- 微信小程序底部实现自定义动态Tabbar
多图警告!!! 最近在工作中遇到这样一个需求:微信小程序底部的Tab需要通过判断登录人的角色动态进行改变,想要实现这个功能依靠小程序原生的Tabbar是不可能实现的了,所以研究了一下自定义Tab,这里 ...
- 2- 计算机的组成以及VMware使用
计算机的组成: 硬件: 处理器(CPU):I3 I5 I7 运行内存RAM(存储数据) 容量(字节为单位) 主板(总线设备) 输入输出设备(显示屏,键盘,鼠标,触目屏) 外部存储设备(硬盘,U盘,TF ...
- ThinkPHP5 利用.htaccess文件的 Rewrite 规则隐藏URL中的 index.php
1.首先修改Apache的httpd.conf文件. 确认httpd.conf配置文件中加载了mod_rewrite.so 模块,加载的方法是去掉mod_rewrite.so前面的注释#号 讲http ...
- 【vue-05】vue-cli
Vue-router官网 安装 vue-router是一个插件包,所以我们还是需要用npm 来进行安装.打开命令行工具,进入你的项目目录,输入下面命令. npm install vue-router ...
- Mongodb安装配置以及在thinkphp中的使用
1.Mongodb安装 Mongodb安装参照此文档: https://www.runoob.com/mongodb/mongodb-window-install.html php的mongodb扩展 ...
- Postman报文进行解密之RSA私钥解密
接口返回的数据也是加密的,需要对数据解密才能看到返回的数据是否正确,就需要用RSA解密. 返回数据的解析可以在postman的Tests进行后置处理,获取加密后的返回数据: var data = JS ...
- Django(13)django时区问题
前言 我们都知道时区,标准时区是UTC时区,django默认使用的就是UTC时区,所以我们存储在数据库中的时间是UTC的时间,但是当我们做的网站只面向国内用户,或者只是提供内部平台使用,我们希望存储在 ...
- python分析《三国演义》,谁才是这部书的绝对主角(包含统计指定角色的方法)
前面分析统计了金庸名著<倚天屠龙记>中人物按照出现次数并排序 https://www.cnblogs.com/becks/p/11421214.html 然后使用pyecharts,统计B ...
- Nios II系统在Quartus II编译后Timing requirements for slow timing model timing analysis were not met. See Report window for details
来自http://wenku.baidu.com/link?url=h0Z_KvXD3vRAn9H8mjfbVErVOF_Kd3h-BZSyF1r4sEYj3ydJGEfBHGY1mvntP4HDuF ...