1: Mybatis简介 1.1: 什么是mybatis Mybatis是由apache提供的一个针对持久层开源框架,对JDBC访问数据库的过程进行了简化和封装 使用mybatis可以只关注SQL语句本身,而不需要关注(JDBC中的)注册驱动、获取连接、获取传输器、释放资源等过程。 mybatis可以将要执行的SQL语句使用xml文件的方式或者注解方式配置起来,在执行时,将Java对象中携带的参数值和SQL骨架进行映射,生成最终要执行的SQL,将执行的结果处理后再返回。 1.2: mybatis的优势 1)JDBC连接访问数据库有大量重复的代码,而mybatis可以极大的简化JDBC代码 2)JDBC没有自带连接池,而mybatis自带的有连接池 3)JDBC中是将SQL语句、连接参数写死在程序中,而mybatis是将SQL语句以及连接参数都写在配置文件中。 4)JDBC执行查询后得到的ResultSet我们需要手动处理,而mybatis执行查询后得到的结果会处理完后,将处理后的结果返回。 2: Mybatis快速入门 2.1: 准备数据(创建yonghedb库,并在其中创建emp表,并往emp中插入8条记录) -- ------------------------------------------ -- 1、创建数据库 yonghedb 数据库 create database if not exists yonghedb charset utf8; use yonghedb; -- 选择yonghedb数据库 -- 2、删除emp表(如果存在) drop table if exists emp; -- 3、在 yonghedb 库中创建 emp 表 create table emp( id int primary key auto_increment, name varchar(50), job varchar(50), salary double ); -- 4、往 emp 表中, 插入若干条记录 insert into emp values(null, '王海涛', '程序员', 3300); insert into emp values(null, '齐雷', '程序员', 2800); insert into emp values(null, '刘沛霞', '程序员鼓励师', 2700); insert into emp values(null, '陈子枢', '部门总监', 4200); insert into emp values(null, '刘昱江', '程序员', 3000); insert into emp values(null, '董长春', '程序员', 3500); insert into emp values(null, '苍老师', '程序员', 3700); insert into emp values(null, '韩少云', 'CEO', 5000); -- ------------------------------------------ 2.2: 创建项目、导入jar包、创建测试类 1)创建一个maven的Java项目: CGB-Mybatis-01 2)在项目的pom文件中导入mybatis的、mysql驱动的 以及 log4j的jar包 -- ------------------------------------------ <dependencies> <!-- junit单元测试 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.9</version> </dependency> <!-- mysql驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.11</version> </dependency> <!-- mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.2.8</version> </dependency> <!-- 整合log4j --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.6.4</version> </dependency> </dependencies> -- ------------------------------------------ 3)创建一个 cn.tedu.MybatisTest01 类, 在这个类中查询emp表中的所有数据, 并将所有员工数据封装到Emp对象中, 最后将Emp对象封装到List集合中 4)提供[mybatis-config.xml]文件, 在这个文件中配置事务管理方式, 以及连接数据库的基本信息, 以及是否使用连接池等 mybatis-config文件头信息如下: --------------------------- <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <!-- MyBatis的全局配置文件 --> <configuration> </configuration> --------------------------- mybatis-config.xml文件的完整内容如下: -------------------------------------------- <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <!-- MyBatis的全局配置文件 --> <configuration> <!-- 1.配置开发环境 --> <environments default="dev"> <environment id="dev"> <!-- 1.1.配置事务管理方式 JDBC: 将事务交给JDBC管理(mybatis会自动开启事务,但需手动提交) MANAGED: 自己手动管理事务 --> <transactionManager type="JDBC"></transactionManager> <!-- 1.2.配置连接池信息, type的取值: JNDI: 已过时 UNPOOLED: 不使用连接池 POOLED: 使用连接池(可以减少连接创建次数,提高执行效率) --> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql:///yonghedb?characterEncoding=utf-8&serverTimezone=Asia/Shanghai"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> <!-- 2.导入XxxMapper.xml文件(如果mapper文件有多个, 可以通过多个mapper标签导入) resource属性会直接到类目录(classes)下去找指定位置的文件 --> <mappers> <mapper resource="EmpMapper.xml"/> </mappers> </configuration> -------------------------------------------- 5)提供[EmpMapper.xml]文件, 在这个文件中可以配置我们想执行的任何SQL语句(查询,新增,修改,删除等) EmpMapper文件头信息如下: --------------------------- <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace=""> </mapper> --------------------------- EmpMapper.xml文件的完整内容如下: -------------------------------------------- <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- namespace: 用于标识当前这个mapper文件(就是一个名字) 在mybatis程序中需要通过这个名字来定位当前这个mapper文件 通过namespace值+id值可以定位要执行的是哪条SQL语句 --> <mapper namespace="EmpMapper"> <!-- 通过select,insert,update,delete标签来存放要执行的SQL --> <!-- 练习01: 查询emp表中的所有员工信息 --> <!-- id属性:要求当前这个文件中的id值必须是独一无二的(不能重复) resultType属性: 指定查询的结果要存放在哪个类型的对象中 --> <select id="findAll" resultType="cn.tedu.pojo.Emp"> select * from emp </select> </mapper> -------------------------------------------- 6)提供一个 cn.tedu.pojo.Emp 类,用于封装查询的员工信息,Emp代码如下: -------------------------------------------- public class Emp { //提供私有属性 private Integer id; private String name; private String job; private Double salary; //提供属性对应的get和set方法 public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getJob() { return job; } public void setJob(String job) { this.job = job; } public Double getSalary() { return salary; } public void setSalary(Double salary) { this.salary = salary; } //toString方法 @Override public String toString() { return "Emp [id=" + id + ", name=" + name + ", job=" + job + ", salary=" + salary + "]"; } } -------------------------------------------- 为了封装员工信息而提供的Emp类, 我们称之为实体类(简单java对象, POJO: Plain Ordinary Java Object) 如果要查询所有的员工信息,员工信息查询出来后需要封装到Java对象中 因此这里需要提供的Emp(员工)类,这个类用于封装所有的员工信息 pojo(plain old/ordinary java object): 简单java对象 如果一个类只是用来封装数据的(比如为了封装员工信息而提供的Emp类) 这样的类我们称之为POJO类,通过该类生成的对象称之为POJO对象 有哪些信息(数据)需要封装到Emp类中,就在Emp类中提供什么的属性/变量 在Emp中提供4个变量(id、name、job、salary)分别用来封装emp表中的id、name、job、salary四列数据。 再提供4个变量(id、name、job、salary)对应的Get和Set方法 关于Emp实体类中的id和salary为什么不用int和double类型, 而是用Integer和Double类型 因为int和double都属于基本类型, 基本类型的默认值(0, 0.0, 空字符,false ) 但如果使用包装类型, 包装类型的默认值是null, 可以避免一些误会产生 重写toString:是为了在打印Emp对象时,输出的是对象中所存储的值,而不是输出地址值 在编写实体类(POJO类)时,如果要添加有参构造函数,建议将无参构造函数也带上! -------------------------------------------- 7)完成 cn.tedu.MybatisDemo1中的代码, 并运行测试 -------------------------------------------- public class MybatisDemo01 { public static void main(String[] args) throws Exception { //1.读取mybatis核心配置文件中的配置信息(mybatis-config.xml) InputStream in = Resources.getResourceAsStream( "mybatis-config.xml" ); //2.基于上面读取的配置信息获取SqlSessionFactory对象(工厂) SqlSessionFactory fac = new SqlSessionFactoryBuilder().build( in ); //3.打开与数据库的连接(即通过工厂对象获取SqlSession对象) SqlSession session = fac.openSession(); //4.通过namespace+id找到并执行SQL语句, 返回处理后的结果 //EmpMapper.xml, List<Emp> List<Emp> list = session.selectList( "EmpMapper.findAll" ); //5.输出结果 for (Emp emp : list) { System.out.println( emp ); } } } -------------------------------------------- 8)执行结果: -------------------------------------------- log4j:WARN No appenders could be found for logger (org.apache.ibatis.logging.LogFactory). log4j:WARN Please initialize the log4j system properly. log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info. -- 上面这些是log4j的警告信息, 不是异常, 也不是错误!!! Emp [id=1, name=王海涛, job=程序员, salary=3300.0] Emp [id=2, name=齐雷, job=程序员, salary=2800.0] Emp [id=3, name=刘沛霞, job=程序员鼓励师, salary=2700.0] Emp [id=4, name=陈子枢, job=部门总监, salary=4200.0] Emp [id=5, name=刘昱江, job=程序员, salary=3000.0] Emp [id=6, name=董长春, job=程序员, salary=3500.0] Emp [id=7, name=苍老师, job=程序员, salary=3700.0] Emp [id=8, name=韩少云, job=CEO, salary=5000.0] -------------------------------------------- 2.3: mybatis程序中常见的问题: 1) Emp类中添加了有参构造函数,但没有添加无参构造函数! Mybatis底层会基于反射创建Emp类的对象实例, 创建时使用的就是Emp类的无参构造函数,如果没有无参构造函数,就会报错! 错误信息: java.lang.NoSuchMethodException: cn.tedu.pojo.Emp.<init>() -- 经验总结: 在写实体类时如果要添加有参构造函数, 尽量将无参构造也加上! 2) 执行SQL语句时, namespace或id写错了, 找不到要执行的SQL语句就会报错: 每条SQL语句都有对应的 namespace+id, 而这些信息是存放在一个map中 EmpMapper.findAll(key) : select * from emp(value) 错误信息: Mapped Statements collection does not contain value for EmpMapper.finaAll 如果mapper文件中标签的id值重复了, 将会报如下错误: 错误信息: Mapped Statements collection already contains value for EmpMapper.update 3) 连接数据库的基本信息书写错误, 会导致连接不上数据库: 错误信息: DataSourceException: Unknown DataSource property: usename -- 报了一个未知的数据源(连接池)属性: usename 2.4: 如果emp表中的列名 和 Emp对象中的属性名不相同, 查询的结果能封装到Emp对象中吗? emp表中的列名是:empname, 而Emp对象中的属性名是:name set方法: setEmpname setName 方法不对应 暴力反射: empname name 属性名和列名也不对应 所以数据没法封装! mybatis通过反射根据emp表中的列名(empname) 生成一个set方法: setEmpname 去Emp对象中寻找这个setEmpname, 如果有就调用这个方法将 empname列中的值封装到Emp对象 如果Emp对象中没有setEmpname方法, 就会根据列名(empname) 到Emp对象中寻找是否有同名的 属性, 如果有, 会通过暴力反射将emp表中empname列的值赋值给同名的属性, 但其实也没有! 3: mybatis增删改操作 1.新增员工信息: 赵云 保安 6000 2.修改员工信息: 赵云 保镖 20000 3.删除员工信息: 删除名称为'赵云'的员工信息 ------------------------------------------------- SqlSession session = null; /* @Before标记的方法会在 每个@Test标记的方法之前执行! */ @Before public void beforeMethod() throws Exception{ //1.读取mybatis核心配置文件中的配置信息(mybatis-config.xml) InputStream in = Resources.getResourceAsStream( "mybatis-config.xml" ); //2.基于上面读取的配置信息获取SqlSessionFactory对象(工厂) SqlSessionFactory factory = new SqlSessionFactoryBuilder().build( in ); //3.打开与数据库的连接(即通过工厂对象获取SqlSession对象) session = factory.openSession( true ); //true: 表示自动提交事务, 默认是false, 表示关闭自动提交, 需要手动提交! } ------------------------------------------------- <!-- 练习02:新增员工信息: 赵云 保安 6000 --> <insert id="insert"> insert into emp value(null, '赵云', '保安', 6000 ) </insert> /* 练习02:新增员工信息: 赵云 保安 6000 */ @Test public void testInsert() { //通过namespace+id找到并执行SQL语句, 返回执行结果 int rows = session.insert( "EmpMapper.insert" ); System.out.println( "影响行数为: "+rows ); //提交事务 //session.commit(); } ------------------------------------------------- <!-- 练习03:修改员工信息: 赵云 保镖 20000 --> <update id="update"> update emp set job='保镖', salary=20000 where name='赵云' </update> /* 练习03:修改员工信息: 赵云 保镖 20000 */ @Test public void testUpdate() { //通过namespace+id找到并执行SQL语句, 返回执行结果 int rows = session.update( "EmpMapper.update" ); System.out.println( "影响行数为: "+rows ); } ------------------------------------------------- <!-- 练习04:删除员工信息: 删除名称为'赵云'的员工信息 --> <update id="update"> delete from emp where name='赵云' </update> /* 练习04:删除员工信息: 删除名称为'赵云'的员工信息 */ @Test public void testDelete() { //通过namespace+id找到并执行SQL语句, 返回执行结果 int rows = session.delete( "EmpMapper.delete" ); System.out.println( "影响行数为: "+rows ); } ------------------------------------------------- 4: Mybatis的占位符 4.1.启用log4j日志框架 Log4j是专门为Java语言提供的一个日志框架, 可以通过log4j打印程序中的日志信息 mybatis默认已经支持了log4j框架, 我们在mybatis中使用log4j时, 只需要两步: 1)导入log4j的jar包 2)导入log4j的配置文件(log4j.properties)即可, 配置文件内容如下: ------------------------------------------------- # Global logging configuration log4j.rootLogger=DEBUG, stdout # Console output... log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n ------------------------------------------------- # Global logging configuration log4j.rootLogger=DEBUG, Console, LOGFILE # Console output... log4j.appender.Console=org.apache.log4j.ConsoleAppender log4j.appender.Console.layout=org.apache.log4j.PatternLayout log4j.appender.Console.layout.ConversionPattern=%5p [%t] %d{yyyy-MM-dd hh:mm:ss} - %m%n log4j.appender.LOGFILE=org.apache.log4j.FileAppender log4j.appender.LOGFILE.file=./mylog.log log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout log4j.appender.LOGFILE.layout.ConversionPattern=%5p [%t] %d{yyyy-MM-dd hh:mm:ss} - %m%n ------------------------------------------------- 4.2: mybatis的占位符 mybatis中有两种占位符, 分别是: #{}, ${} 其中最常用的就是 #{} 占位符 示例: ------------------------------------------------- <!-- 练习05:新增员工信息: null, 马云, 教师, 800 --> <insert id="insert2"> insert into emp value( null, #{name}, #{job}, #{salary} ) </insert> /* 练习05:新增员工信息: null, 马云, 教师, 800 */ @Test public void testInsert2() { //声明一个map集合,将SQL参数封装到map中 Map map = new HashMap(); map.put( "name", "马云" ); map.put( "job", "教师" ); map.put( "salary", 800 ); //根据namespace+id找到SQL语句, 将map作为参数传过去, 并执行SQL语句 int rows = session.insert( "EmpMapper.insert2", map ); System.out.println( "影响行数: "+rows ); } ------------------------------------------------- <!-- 练习06: 修改员工信息: 将马云的职位改为"CEO", 薪资改为80000 --> <update id="update2"> update emp set job=#{job}, salary=#{salary} where name=#{name} </update> @Test public void testUpdate3() { //将SQL语句中的参数封装到POJO对象中 //Emp emp = new Emp(null, "马云", "教授", 8888.0); Emp emp = new Emp(); emp.setName( "马云" ); emp.setJob( "教授" ); emp.setSalary( 8888.0 ); //定位SQL并执行SQL session.update( "EmpMapper.update2", emp ); } ------------------------------------------------- 1、#{}占位符: 其实就是JDBC中的问号(?)占位符,在mybatis底层会将 #{}占位符翻译成问号(?)占位符 如果在SQL语句中占位符只有一个#{}占位符,{}中名称没有要求, 但不能是空的; 参数可以直接传递,不用封装; 如果在SQL语句中的#{}占位符,不止一个,参数值需要通过Map或者POJO对象进行封装; 1)如果通过Map集合来封装SQL参数值,#{}占位符中的名称要和Map中的key保持一致! ------------------------------------------------- //声明一个map集合,将SQL参数封装到map中 Map map = new HashMap(); map.put( "name", "马云" ); map.put( "job", "教师" ); map.put( "salary", 800 ); //根据namespace+id找到SQL语句, 将map作为参数传过去, 并执行SQL语句 int rows = session.insert( "EmpMapper.insert2", map ); <!-- 练习05:新增员工信息: null, 马云, 教师, 800 --> <insert id="insert2"> insert into emp value( null, #{name}, #{job}, #{salary} ) </insert> ------------------------------------------------- 因为在mybatis底层是通过#{}占位符中的名称,作为key,到map中获取对应的value; 2)如果通过POJO对象来封装SQL参数值,#{}占位符中的名称要在POJO对象中有对应的getXxx方法,或者有对应的变量 ------------------------------------------------- Emp emp = new Emp(); emp.setName( "马云" ); emp.setJob( "教授" ); emp.setSalary( 8888.0 ); //定位SQL并执行SQL session.update( "EmpMapper.update2", emp ); <!-- 练习06: 修改员工信息: 将马云的职位改为"CEO", 薪资改为80000 --> <update id="update2"> update emp set job=#{job}, salary=#{salary} where name=#{name} </update> ------------------------------------------------- 例如:#{job}占位符中的名称为job,那么就意味着,在Emp中要有getJob()方法或者有job变量,如果两者都有,会优先通过getXxx方法来获取POJO对象中存储的属性值,如果没有getXxx方法,会通过暴力反射直接获取Emp中job变量的值。 总结: 在Mybatis框架中,大部分情况都是用#{}占位符,#{}其实就是JDBC中的问号(?)占位符,是为SQL语句中的【参数值】进行占位。例如: 查询:select * from emp where job=参数值 and salary>参数值 新增: insert into emp value(null, 参数值, 参数值, 参数值) 修改: update emp set 列=参数值, 列=参数值, .. where 列=参数值... 删除: delete from emp where 列=参数值... 2、${}占位符: select * from emp where id>5; ${}占位符: 是为SQL语句中的某一个SQL片段进行占位,将参数传递过来时,是直接将参数拼接在${}占位符所在的位置,因为是直接拼接,所以可能会引发SQL注入攻击,因此不推荐大量使用! 如果SQL语句中只有一个#{}占位符,参数可以不用封装,直接传递即可! 但如果SQL语句中哪怕只有一个${}占位符,参数也必须得先封装到Map或者POJO对象中,再把Map或者POJO对象传递过去! mybatis底层在执行SQL语句时,使用的就是PreparedStatement对象来传输SQL语句! ------------------------------------------------- <!-- 练习09: 查询emp表中的所有员工信息, 动态显示要查询的列 --> <select id="findAll2" resultType="cn.tedu.pojo.Emp"> select ${colName} from emp </select> /* 练习09: 查询emp表中的所有员工信息, 动态显示要查询的列 * select * from emp; "*" * select id, name, job; "id,name,job" */ @Test public void testFindAll2() { Map map = new HashMap(); map.put( "colName" , "name,job" ); List<Emp> list = session.selectList( "EmpMapper.findAll2", map ); for (Emp emp : list) { System.out.println( emp ); } } ------------------------------------------------- 5: Mybatis的动态SQL if标签、where标签、foreach标签 动态SQL指的是:SQL语句可以根据参数值的不同,来生成不同的SQL语句 ------------------------------------------------- 如果在员工列表页面中可以根据员工的薪资区间查询员工信息 如果不传最低薪资 和 最高薪资, 默认查询所有薪资的员工信息 select * from emp; 如果传了一个最低薪资, 但没有传最高薪资: select * from emp where salary > 最低薪资 如果传了一个最高薪资, 但没有传最低薪资: select * from emp where salary < 最高薪资 如果既传了最低薪资, 也传了最高薪资: select * from emp where salary<最高薪资 and salary>最低薪资 1)if标签: 如果test属性中的布尔表达式为true, 那if标签中的SQL片段就会参与整个SQL语句的执行, 反之, 如果test属性中的布尔表达式为false, 那么if标签的中内容将不会执行! <if test="布尔表达式"> SQL片段 </if> ------------------------------------------------- <!-- 练习10: 根据员工的薪资区间查询员工信息 --> <select id="findBySal" resultType="cn.tedu.pojo.Emp"> select * from emp where 1=1 <if test="minSal != null"> and salary > #{minSal} </if> <if test="maxSal != null"> and salary < #{maxSal} </if> </select> /* 练习10: 根据员工的薪资区间查询员工信息 */ @Test public void testFindBySal() { Map map = new HashMap(); map.put( "minSal" , 3000 ); map.put( "maxSal" , 4500 ); List<Emp> list = session.selectList( "EmpMapper.findBySal", map ); for (Emp emp : list) { System.out.println( emp ); } } ------------------------------------------------- 2)where标签: 将if标签以及其中的条件都包裹在where标签内部, 只要有任何一个条件成立, where标签就会生成where关键字, 并且如果条件前面有多余的连接词(比如or,and),where标签会将多余的连接词去掉! <select id="findBySal2" resultType="cn.tedu.pojo.Emp"> select * from emp <where> <if test="minSal != null"> salary > #{minSal} </if> <if test="maxSal != null"> and salary < #{maxSal} </if> </where> </select> @Test public void testFindBySal2() { Map map = new HashMap(); //map.put( "minSal" , 3000 ); map.put( "maxSal" , 4500 ); List<Emp> list = session.selectList( "EmpMapper.findBySal2", map ); for (Emp emp : list) { System.out.println( emp ); } } ------------------------------------------------- 3)foreach标签 foreach标签可以对传过来的数组或集合进行遍历,生成我们所需要的SQL片段 collection属性:如果传过来的只有一个数组(或List集合) collection指定的值则为array(或list); 如果传过来的是一个map集合, 将数组或集合作为value封装到map中, collection属性的值就是数组或集合在map中的key; open属性:指定所生成SQL片段的起始符号,通常是左圆括号 ( close属性:指定所生成SQL片段的结束符号,通常是右圆括号 ) item属性:指定占位符中的名称 separator属性:指定占位符中间的分隔符, 通常是逗号 , ------------------------------------------------- 案例1:根据员工的id批量删除员工信息 id数组 int ids = {1,3,5,7}; <!-- 练习12: 根据员工的id批量删除员工信息 id数组 int[] ids = {1,3,5,7}; delete from emp where id in(1,3,5,7) delete from emp where id in(?, ?, ?, ?) --> <delete id="deleteByIds"> delete from emp where id in <foreach collection="list" open="(" item="id" separator="," close=")"> #{id} </foreach> </delete> /* 练习12: 根据员工的id批量删除员工信息 id数组 int[] ids = {1,3,5,7}; */ @Test public void testDeleteByIds() { //int[] ids = {1,3,5,7}; //从页面上传过来的员工的id数组 List list = new ArrayList(); list.add( 2 ); list.add( 4 ); list.add( 6 ); list.add( 8 ); session.delete( "EmpMapper.deleteByIds", list ); } ------------------------------------------------- 案例2:根据员工的id批量更新员工信息 id数组 int ids = {2,4,6,8} 1000; ------------------------------------------------- <!-- 练习13: 根据员工的id批量更新员工的薪资, id数组 int[] ids = {2,4,6,8} 1000; map.put("arrayIds", ids); map.put("money", 1000); --> <update id="updateByIds"> update emp set salary=salary + #{money} where id in <foreach collection="arrayIds" open="(" item="id" separator="," close=")"> #{id} </foreach> </update> /* 练习13: 根据员工的id批量更新员工的薪资, id数组 int[] ids = {2,4,6,8} 1000; map.put("arrayIds", ids); map.put("money", 1000); */ @Test public void testUpdateByIds() { int[] ids = {2,4,6,8}; //要更新的员工的id数组 Double money = 1000.0; //要涨的薪资 Map map = new HashMap(); map.put( "arrayIds" , ids ); map.put( "money" , 1000 ); session.update( "EmpMapper.updateByIds", map ); } ------------------------------------------------- 6: Mybatis的Mapper接口开发 mapper接口开发要满足以下四个规则: 1)写一个接口, 要求接口的全类名(包名.接口名) 要等于 XxxMapper文件的namespace值 namespace = 接口的全类名 2)mapper文件中要执行的SQL语句, 在接口中得有对应的接口方法, 并且, SQL语句的id值要等于接口的这个方法名 SQL标签的id值 = 方法名 3)如果是查询SQL, resultType指定的类型要和接口方法的返回值类型对应 a)如果接口返回的是某一个实体对象(Emp), 此时resultType指定的是该对象的类型(cn.tedu.pojo.Emp) b)如果接口返回的是集合(List<Emp>), 此时resultType指定的是集合中的泛型(cn.tedu.pojo.Emp) 4)(可以忽略) SQL标签上的参数类型(paramterType属性)要和 接口方法的参数类型保持一致! ------------------------------------------------- mapper接口开发测试1: 查询emp表中的所有员工信息 1)提供一个接口: cn.tedu.dao.EmpMapper, 将EmpMapper.xml文件中的namespace值改为接口的全类名 //接口的全类名: cn.tedu.dao.EmpMapper public interface EmpMapper { } namespace值: <mapper namespace="cn.tedu.dao.EmpMapper"> 2)要执行的SQL语句的id值 要和接口中的方法名保持一致 <select id="findAll" resultType="cn.tedu.pojo.Emp"> select * from emp </select> public interface EmpMapper { /* 练习01: 查询emp表中的所有员工信息 */ public List<Emp> findAll(); } 3)resultType指定的类型要和接口方法的返回值类型对应 <select id="findAll" resultType="cn.tedu.pojo.Emp"> select * from emp </select> 由于上面查询的emp信息结果可能不止一条, 所以返回的结果需要使用List集合进行封装 public List<Emp> findAll(); 4)SQL语句中没有占位符, 也就不需要传参数, 因此接口方法也就不需要参数! ------------------------------------------------- 模拟mybatis提供的EmpMapper接口的实现类,代码如下: public class EmpMapperImpl implements EmpMapper{ private SqlSession session; //将session对象通过构造方法保存到类的内部 public EmpMapperImpl( SqlSession session ) { this.session = session; } /* 查询所有的员工信息 */ public List<Emp> findAll() { /* 获取当前这个类的父接口的全类名(=namespace) 通过当前类的对象(this)获取当前类的字节码对象, 再通过当前类的字节码对象获取当前类实现的所有 父接口组成的数组, 由于当前类实现的接口只有一个, 所有通过数组[0]获取当前类实现的接口 再通过接口获取接口的全类名(包名.接口名) */ String interName = this.getClass().getInterfaces()[0].getName(); //获取当前方法的名字(=SQL标签的id值) /* 获取存放的方法调用栈信息(也就是所有方法的调用信息), 返回的是一个数组, getStackTrace 这个方法会在栈顶(也就是数组的第一个元素), 调用这个方法的是findAll方法, 这个方法在数组中的 第二个位置(也就是数组的第二个元素) */ StackTraceElement[] st = Thread.currentThread().getStackTrace(); /* 通过数组的中的第二个元素(其中方法所属类的全类名,方法名,当前方法所属文件的名字,也就方法调用的行数) 获取当前调用的方法的名字 */ String methodName = st[1].getMethodName(); List<Emp> list = session.selectList( interName+"."+methodName ); //System.out.println("----------------------------"); //for (StackTraceElement stackTraceElement : st) { // System.out.println( stackTraceElement ); //} //System.out.println("----------------------------"); //System.out.println("下面获取方法调用栈数组中第二个元素中的所有信息: "); //System.out.println( "获取当前方法的类名:"+st[1].getClassName() ); //System.out.println( "获取当前方法的名字:"+st[1].getMethodName() ); //System.out.println( "获取当前方法所属的文件名:"+st[1].getFileName() ); //System.out.println( "获取当前方法调用的行数:"+st[1].getLineNumber() ); //System.out.println("----------------------------"); return list; } @Override public void insert() { //获取当前这个类的父接口的全类名(=namespace) String interName = this.getClass().getInterfaces()[0].getName(); //获取当前方法的名字(=SQL标签的id值) StackTraceElement[] st = Thread.currentThread().getStackTrace(); String methodName = st[1].getMethodName(); session.insert( interName+"."+methodName ); } } 测试自己提供的EmpMapper接口的实现类(EmpMapperImpl) ------------------------------------------------- /* 测试自己提供的EmpMapper接口的实现类: 新增员工信息 */ @Test public void testInsert2() { //获取EmpMapper接口的子类(我们自己提供)的对象实例 EmpMapper mapper = new EmpMapperImpl(session); mapper.insert(); } /* 测试自己提供的EmpMapper接口的实现类(EmpMapperImpl) */ @Test public void testFindAll2() { //获取EmpMapper接口的子类(我们自己提供)的对象实例 EmpMapper mapper = new EmpMapperImpl(session); List<Emp> list = mapper.findAll(); for (Emp emp : list) { System.out.println( emp ); } } ------------------------------------------------- 7: mybatis的注解开发 使用xml方式和注解开发的区别? 1)使用xml方式配置SQL语句,写起来相比注解要麻烦一些; 而注解方式,不用写配置文件,直接在接口的方法上面添加一个注解(@Select,@Insert,@Update,@Delete..),将SQL语句直接写在注解的括号里即可; 2)使用注解方式配置SQL语句又回到将SQL语句写在Java程序中,如果将来SQL语句一旦发生变化,就意味着要修改java源文件(.java),改完后要重新编译整个项目,再打包、部署到服务器上,相比来说较为麻烦。 1.由于使用注解开发,要将SQL语句配置在注解中,因此EmpMapper.xml文件也就不需要了,在mybatis-config.xml文件中也就不需要引入EmpMapper.xml文件了 2.在mybatis-config.xml文件中引入SQL语句所在的mapper接口(因为SQL语句存放在接口中) <mappers> <!-- <mapper resource="EmpMapper.xml"/> --> <!-- 可以将每一个XxxMapper接口通过一个mapper标签的class属性引入 如果有多个XxxMapper接口,添加多个mapper标签引入即可! --> <!-- <mapper class="cn.tedu.dao.EmpMapper"/> --> <!-- 或者是直接引入XxxMapper接口所在的包, 这样mybais会直接扫描这个包下的 所有接口中的每个方法, 那么这些方法上所配置的SQL语句也会被读取到mybatis中 --> <package name="cn.tedu.dao"/> </mappers> 3.将SQL语句移动到EmpMapper.java接口中 ------------------------------------------------- //接口的全类名: cn.tedu.dao.EmpMapper public interface EmpMapper { /* 练习01: 查询emp表中的所有员工信息 */ @Select("select id,name,job from emp") public List<Emp> findAll(); /* 练习02: 新增员工信息(没有占位符) */ @Insert("insert into emp value(null, '赵云云', '高级Java工程师', 35000)") public void insert(); /* 练习05:新增员工信息: null, 马云, 教师, 800(有占位符) */ @Insert("insert into emp value(null, #{name}, #{job}, #{salary})") public void insert2(Map map); /* 练习06: 修改员工信息: 将马云的职位改为"CEO", 薪资改为80000(有占位符) */ @Update("update emp set job=#{job}, salary=#{salary} where name=#{name}") public void update2(Emp emp); } ------------------------------------------------- 4.测试(由于这里只是将SQL语句从XxxMapper.xml文件中移动到了XxxMapper接口中, 所以测试代码直接使用之前的即可!) 8: 补充内容 单元测试框架(junit): 这个框架可以在不提供main函数, 并且不创建对象的情况下, 直接运行一个非静态的方法 @Test注解可以标记一个非静态的方法, 在不添加main函数并且不创建对象的情况下直接运行这个方法, 但被@Test标记的方法必须符合以下要求: 1)方法必须是公共的(pubilc), 不能是私有的(private) 2)方法必须是非静态的(no static) 3)方法必须是无返回值的(no return) 4)方法必须是无参数的(no param) 如果违反以上任何一个规则, 就会报如下异常: java.lang.Exception: No tests found matching [{ExactMatcher:...
======================================== >> tomcat服务器 ======================================== 一、服务器相关概念 1、什么是服务器 服务器:分为软件服务器和硬件服务器 硬件服务器:运行在互联网上的、具有静态IP的一台计算机(通常配置比较高) 软件服务器:运行在互联网上的计算机程序(软件),将服务器软件安装在硬件服务器上,才可以对外提供服务。 服务器软件分为很多种:数据库服务器(MySQL,Oracle,SQL Server等),Web服务器(tomcat,jetty,jboss等),邮件服务器,FTP服务器。。。 2、什么Web服务器 Web服务器: 运行在互联网上的计算机程序,专门用于接收客户端(主要指浏览器)的请求,根据请求进行处理,最后给出回应! 比如:打开浏览器,输入"http://www.baidu.com"回车,其实访问的就是百度的服务器,此时会向百度服务器发送一个请求,请求百度的首页,百度服务器会接收并处理这个请求,根据请求给出回应(将百度首页响应给客户端浏览器) tomcat就是一个Web服务器,特点是:小巧灵活,简单易用,学习成本非常低! 二、Tomcat服务器下载、安装、启动、配置 1、下载tomcat服务器 下载地址: http://tomcat.apache.org tomcat分为很多版本,有windows版本(解压版和安装版)、linux版本 推荐使用解压版(需要用的时候解压一份,不需要用了直接删除解压的目录即可!) 2、tomcat的安装、启动、配置 2.1.安装tomcat 安装:解压之后就可以使用(安装的路径中最好不要包含中文和空格) 在启动tomcat之前,需要配置(检查)一个JAVA_HOME环境变量,该变量需要指向JDK的安装根目录 变量名:JAVA_HOME 变量值:D:\software\Java\jdk1.8.0_161 (此处换成你安装的JDK的根目录) 由于tomcat服务器是由Java语言开发的,所以运行tomcat需要JDK的支持 JAVA_HOME这个变量就是在告诉tomcat服务器,需要使用哪一个位置上的JDK. 2.1.启动tomcat 启动tomcat: 通过[tomcat安装目录]/bin/startup.bat文件可以启动tomcat服务器; 关闭tomcat: 通过[tomcat安装目录]/bin/shutdown.bat文件可以关闭tomcat服务器; 或者直接点击右上角的叉号 启动tomcat之后,可以打开浏览器,访问: http://localhost:8080 http://127.0.0.1:8080 如果可以访问到tomcat服务器的主页,就说明tomcat安装并且启动成功了! 3.修改tomcat服务器默认端口 如果不修改端口,每次在访问tomcat服务器时,都需要在[主机名/ip地址]的后面加上:8080 如果想在访问时,在主机名或ip地址后面省略端口,可以将端口修改为80(这个端口特殊,可以省略不写!) 修改端口的方法是: 找到[tomcat安装目录]/conf/server.xml并用文本编辑工具打开这个文件 找到文件的69行,将Connector标签上的port属性值改为80,保存文件,并重启服务器即可生效! 重启服务器后,就可以通过如下路径访问tomcat服务器: http://localhost:80 http://localhost http://127.0.0.1:80 http://127.0.0.1 三、tomcat服务器的目录结构(了解) bin: 存放批处理文件的目录(startup.bat、shutdown.bat文件) conf: 存放tomcat配置文件的目录(server.xml是tomcat核心配置文件) lib: 存放tomcat服务器在运行时所依赖的jar包的目录 logs: 存在tomcat服务器在运行时产生的日志文件的目录 temp: 存放tomcat服务器在运行时产生的临时文件的目录 work: 存放tomcat服务器在运行期间产生的一些工作文件 (JSP在第一次被访问时翻译后的Servlet文件、 session对象序列化后产生的文件等都会放在这个目录下) webapps: 是Web应用的存放目录,放在这个目录中的Web应用程序, 可以通过localhost虚拟主机进行访问 webapps目录是localhost主机默认存放Web应用的目录 把Web应用放在webapps目录下,就相当于发布到了localhost主机中 四、Web应用和虚拟主机 1、Web应用: Web应用其实就是一个目录,其中可以包含很多资源文件(html/css/js/图片/jsp/servlet..等) 虚拟主机中不能直接管理Web资源文件(html/css/js/图片/jsp..等) 需要将Web资源文件组织成一个Web应用(目录),将Web应用发布到虚拟主机中运行才可以被虚拟主机所管理 2、虚拟主机: 就是在tomcat服务器中配置的一个站点,在访问时就好像在访问一台真实独立的主机一样 我们将这个站点称之为是,运行在tomcat服务器中的一台虚拟主机 tomcat服务器中可以配置多个站点,每一个站点都是一台虚拟主机。 下面是tomcat默认提供的localhost主机的配置: <Host name="localhost" appBase="webapps"...></Host> <Host name="www.baidu123.com" appBase="baidu"...></Host> ======================================================== 1)在服务器硬件上安装了一个tomcat服务器软件 2)在tomcat服务器软件内部可以配置多个站点(虚拟主机),其中tomcat默认自带了一个localhost虚拟主机。 3)localhost虚拟主机默认管理Web应用的目录--webapps,发布到webapps目录下的web应用,也就都发布到了localhost主机中 4)往webapps中发布了一个 jt web应用,其中包含一些Web资源文件 5)web资源文件可以是(html/css/js/图片/servlet/jsp等) 五、Web应用 1、Web应用的目录结构 news(目录,Web应用) |-- 也可以将Web资源文件放在Web应用的根目录下 |-- 其它目录(放在其它目录中的资源文件可以被浏览器直接访问到) |-- WEB-INF目录(隐私目录,放在这里面的资源文件,不能被浏览器直接访问) |-- classes目录(Java程序编译后的class文件会放在这个目录下) |-- lib目录(Web应用所依赖的jar包会放在这个目录下) |-- web.xml文件(当前Web应用的核心配置文件) 2、如何发布一个Web应用到虚拟主机中 直接将Web应用的目录复制到虚拟主机所管理的目录下即可 例如:将news复制到webapps目录下,由于webapps是localhost主机发布web应用的目录, 所以相当于将news发布到了localhost主机中,可以通过localhost主机进行访问! ======================================== >> 导入已有的Maven项目(web项目,yonghe-ssm) ======================================== 如何导入已有的Maven项目 1: 导入数据库(yonghedb)、表、表记录 打开"yonghe-ssm项目"目录中的"SQL脚本.txt", 复制其中的SQL语句, 到Navicat中执行 执行后会创建yonghedb库, 并创建 tb_door 表(门店表) 和 tb_order(订单表) 2:导入yonghe项目 方式一: 通过import导入(只要是eclipse创建的项目, 任何项目都可以这样导入) 1)点击eclipse左上角的"File" --> "Import...", 在弹出的窗口中找 "General" --> "Existing Project into Workspace" 2)接着会切换到下一个窗口, 可以看到一个 "Select root directory"选项(选择根目录), 这行的后面有 "Browse..." 选项, 点击 "Browse..." 可以找到你要导入的项目, 点击你要导入的项目, 点击下方的 "选择文件夹" 3)接着在窗口中间的位置 找到 "Copy Pojects into workspace" 选项并选中该选项, 选中该选项意味着: 导入的项目会复制到工作空间中一份(这样的好处是, 即使导入位置的项目删除了也不影响, 因为已经把整个项目复制到工作空间中了), 最后点击 "Finish" 完成即可! 方式二: 创建新的项目, 将原项目中的代码复制到新项目中 1)创建一个同类型的新的项目 如果要导入的项目是Maven的Web项目,也就需要你创建一个Maven的Web项目 这里新项目的名字可以 和原项目保持一致, 也可以不一致 2)将原项目中的src目录复制到新项目中, 将新项目中的src目录覆盖即可 3)如果是maven项目, 那么其中还有pom.xml文件, 将原项目中的 pom.xml 文件打开, 将其中需要的配置复制, 粘贴到新项目的pom文件中的对应位置即可! 3: 可能出现的问题 1)导入项目后如果是 xxx.xml 文件报错, 这里的错误可以忽略, 是eclipse误报的! 2)如果在导入项目后,有叹号(!),都是因为maven没有将所有的环境/依赖下载下来。 可以通过如下三个步骤去解决: a)打开yonghe项目,找到其中的pom.xml文件,在文件中敲一个空白行并保存,让maven重新扫描pom文件,并根据其中的配置下载所需要的依赖; b)在报错的maven项目上右键--> Maven--> Update Project...,在弹出的窗口中勾选下方的 []Force Update... 即勾选强制更新,如果还没有解决,再看第3步! c)下载老师下发的本地仓库(localRepo(maven的本地库).zip) 用老师下发的本地仓库,替换自己的本地仓库! 运行导入的yonghe项目,在项目上右键--> Run As --> Run On Server,在打开的浏览器地址栏后面补全路径: http://localhost:8080/yonghe/index /index对应的是controller中的一个方法,最后会跳转到index.jsp ======================================== >> Servlet ======================================== 一、什么是Servlet Servlet 是由SUN公司提供的一门Web资源开发技术(规范,接口) Servlet是本质上是一个Java程序,但和我们之前接触的Java程序不同的是, Servlet无法独立运行(Servlet中没有main函数) 需要将Servlet程序放在服务器中,由服务器调用才可以执行! 运行在服务器中的Servlet程序作用是: 对服务器接收的请求进行处理(处理请求) ------------------------------------------- Servlet3.1的jar包 <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> Servlet2.5的jar包 <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency> ------------------------------------------- 二、开发Servlet程序 1、开发Servlet程序的步骤 1)写一个类,需要实现一个Servlet接口或者继承Servlet接口的子类 Servlet |-- GenericServlet 实现了Servlet接口,并实现了其中的方法 |-- HttpServlet 继承了GenericServlet,也实现了其中的方法 在开发时,我们只需要继承HttpServlet,并继承其中的方法即可! 2)在web.xml文件中配置Servlet对外访问的路径,再将Web应用发布到服务器即可! 如果是Servlet3.0及以上版本, 可以使用注解方式配置Servlet访问路径 2、如何通过Eclipse创建一个Servlet程序 1) Ctrl+N, 会弹出创建窗口, 在输入框中输入 "servlet" 进行搜索, 选中servlet, 点击 "Next" 接着进入下一界面(其实这里和我们之前创建类几乎一样了, 区别就是这里默认会继承 HttpServlet ) 2) 填写包名和类名点击完成即可 3) 创建好的类中有很多不需要的注释和构造方法实现, 删除即可! 创建的Servlet类的内部保留 doGet 和 doPost 方法即可! 3、运行Servlet程序 第一种运行方式: 直接在要运行的文件(html/jsp/servlet等)上-->右键-->Run as-->Run On Server, eclipse会帮我们做如下几件事儿: 1)将Servlet所在的Web项目(CGB-Servlet-01)发布到服务器中 2)再启动tomcat服务器 3)最后打开浏览器,在地址栏输入路径去访问这个Servlet Eclipse默认用内置浏览器去访问Servlet,但是这个内置浏览器有bug,最好是使用本地的浏览器去测试! 第二种运行方式: 可以自己手动将项目发布到服务器、启动服务器、打开浏览器输入地址进行访问! 4、Servlet在web.xml文件中的配置 全限定类名(全类名): 包名.类名 或者 包名.接口名 ------------------------------------------ <servlet> <servlet-name>HelloWorld</servlet-name> <servlet-class>cn.tedu.HelloWorld</servlet-class> </servlet> <servlet-mapping> <servlet-name>HelloWorld</servlet-name> <url-pattern>/HelloWorld</url-pattern> </servlet-mapping> ------------------------------------------ 1)每创建一个Servlet,eclipse会帮我们生成至少8行配置信息 这8行配置信息由一个Servlet标签和一个servlet-mapping标签组成 这两个标签中的<servlet-name>标签中的内容一致,决定了它俩是一组配置 2)<servlet-class>标签中配置的当前Servlet类的全类名(包名.类名) 将来服务器根据访问路径找到这个全类名,再利用反射+全类名可以获取当前Servlet类的实例 3)<url-pattern>标签中配置了外界该通过什么路径来访问当前Servlet。 也就是说,这里配置什么路径,外界就得通过什么路径来访问这个Servlet! 注意事项1: 如果不知道什么原因,tomcat服务器启动失败了,可以将Eclipse创建的Server删除, 再重新创建一份(删除Server同时,也将左侧的Servers项目从工作空间中删除!) 注意事项2: 在将tomcat和Eclipse整合之后,tomcat默认开启了热部署功能: 在修改了代码后,不用重新发布,也不需要重启服务器,就可以运行最新的效果! (如果是创建了新的Servlet类,或者修改了web.xml文件,则需要重启服务器,才会生效!) 5、创建Servlet3.0或以上版本的项目 在Servlet3.0的项目中, 可以通过注解方式配置Servlet相关信息 ------------------------------------------ @WebServlet("/HelloWorld") public class HelloWorld extends HttpServlet{} ------------------------------------------ 在@WebServlet这个注解内部所配置的内容就是(xml方式)url-pattern中配置的访问路径 服务器通过扫描注解定位到当前这个Servlet,获取该类的全路径, 通过全路径从硬盘上加载这个类到内存中,获取该类的字节码对象,再利用反射+字节码对象创建该类的实例,然后通过HelloWorld类的对象实例再调用其中的方法! 代码示例: ------------------------------------------ @WebServlet("/HelloWorld") public class HelloWorld extends HttpServlet { private static final long serialVersionUID = 1L; //Http: GET和POST protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //设置服务器发送数据以及浏览器接收数据的编码 response.setContentType( "text/html;charset=utf-8" ); //向浏览器响应数据 response.getWriter().write( "世界, 你好!" ); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } } ------------------------------------------