完整SSM课件及相关代码
官方文档
MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis,实质上Mybatis对ibatis进行一些改进。
MyBatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,屏蔽了jdbc api底层访问细节,使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码。
Mybatis通过xml或注解的方式将要执行的各种statement(statement、preparedStatemnt、CallableStatement)配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成java对象并返回。
import org.junit.Test; import java.sql.*; /** * description:java原生方式mysql * * @author jiaoqianjin * Date: 2021/3/3 **/ public class MySQLTest { static final String JDBC_DRIVER = "com.mysql.cj.jdbc.Driver"; static final String DB_URL = "jdbc:mysql://localhost:3306/test2?useSSL=false&allowPublicKeyRetrieval=true" + "&serverTimezone=UTC"; // 数据库的用户名与密码,需要根据自己的设置 static final String USER = "root"; static final String PASS = "123456"; /** * 测试原生sql方式查询 */ @Test public void testQuery() { Connection conn = null; Statement stmt = null; try { // 1.注册 JDBC 驱动 Class.forName(JDBC_DRIVER); // 2.打开链接 System.out.println("连接数据库..."); conn = DriverManager.getConnection(DB_URL, USER, PASS); // 3.执行查询 System.out.println(" 实例化Statement对象..."); stmt = conn.createStatement(); String sql; sql = "SELECT id, name, age FROM user WHERE name='小王'"; ResultSet rs = stmt.executeQuery(sql); // 4.展开结果集数据库 while (rs.next()) { // 通过字段检索 int id = rs.getInt("id"); String name = rs.getString("name"); int age = rs.getInt("age"); // 输出数据 System.out.print("ID: " + id); System.out.print("name: " + name); System.out.print("age: " + age); System.out.print("\n"); } // 5.完成后关闭 rs.close(); stmt.close(); conn.close(); } catch (Exception se) { // 处理 JDBC 错误 se.printStackTrace(); }// 处理 Class.forName 错误 finally { // 关闭资源 try { if (stmt != null) { stmt.close(); } } catch (SQLException ignored) { }// 什么都不做 try { if (conn != null) { conn.close(); } } catch (SQLException se) { se.printStackTrace(); } } System.out.println("Goodbye!"); } }
存在的问题
数据库连接频繁开启和关闭,会严重影响数据库的性能。
代码中存在硬编码,分别是数据库部分的硬编码和SQL执行部分的硬编码。
Mybatis的存在:
Mybatis就是帮助我们将数据存入数据库中 , 和从数据库中取数据
MyBatis 是一个半自动化的ORM框架 (Object Relationship Mapping) -->对象关系映射
当然,所有的事情,不用Mybatis依旧可以做到,只是用了它,所有实现会更加简单!
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>x.x.x</version> </dependency>
Github:https://github.com/mybatis/mybatis-3
源码中文注释: https://github.com/tuguangquan/mybatis
要使用 MyBatis, 只需将 mybatis-x.x.x.jar 文件置于类路径(classpath)中即可。
项目结构
CREATE DATABASE `mybatis_01`; USE `mybatis_01`; DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` int(20) NOT NULL, `name` varchar(30) DEFAULT NULL, `phone` varchar(30) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; insert into `user`(`id`,`name`,`phone`) values (1,'张三','1536537156'),(2,'李四','1536537157'),(3,'王五','1536537158');
新建一个Maven项目作为父工程,删除src目录,
<dependencies> <!-- mybatis依赖 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.2</version> </dependency> <!-- 数据库驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <!-- <dependency>--> <!-- <groupId>mysql</groupId>--> <!-- <artifactId>mysql-connector-java</artifactId>--> <!-- <version>8.0.12</version>--> <!-- </dependency>--> <!--测试依赖--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> </dependency> </dependencies> <!--在build中配置resources,来防止我们资源导出失败的问题--> <build> <resources> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>true</filtering> </resource> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>true</filtering> </resource> </resources> </build>
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"> <configuration> <!-- 配置mybatis的环境信息 --> <environments default="development"> <environment id="development"> <!-- 配置JDBC事务控制,由mybatis进行管理 --> <transactionManager type="JDBC"/> <!-- 配置数据源,采用传统的javax. sql. DataSource规范中的连接池 --> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <!-- 如果数据库驱动使用的是8.0.12需要使用下面的配置 --> <!--<property name="url" value="jdbc:mysql://localhost:3306/mybatis_01?useSSL=false&useUnicode=true&serverTimezone=UTC&characterEncoding=utf8"/>--> <property name="url" value="jdbc:mysql://localhost:3306/mybatis_01?useSSL=false&useUnicode=true&characterEncoding=utf8"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <mappers> <mapper resource="com/shida/dao/UserMapper.xml"/> </mappers> </configuration>
MybatisUtil
public class MybatisUtil { private static SqlSessionFactory sqlSessionFactory; static { try { // 1. 读取配置文件 String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); // 2. 通过SqlSessionFactoryBuilder创建sqlSessionFactory会话工厂 sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } /** *功能描述:获取SqlSession连接 * @author jiaoqianjin * @date 2021/3/3 */ public static SqlSession getSession(){ // 使用sqlSessionFactory创建SqlSession return sqlSessionFactory.openSession(); } }
User
public class User { private int id; //id private String name; //姓名 private String phone; //手机号 //构造,有参,无参 //set/get //toString() }
public interface UserMapper { /** *功能描述: 查询用户集合 * @return java.util.List<com.shida.entity.User> * @author jiaoqianjin * @date 2021/3/3 */ List<User> selectUser(); }
<?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:命名空间,它的作用就是对SQL进行分类化管理,可以理解为SQL隔离 注意:使用mapper代理开发时,namespace有特殊且重要的作用 --> <mapper namespace="com.shida.dao.UserMapper"> <select id="selectUser" resultType="com.shida.entity.User"> select * from user </select> </mapper>
public class UserTest { @Test public void selectUser() { SqlSession session = MybatisUtil.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); // 调用SqlSession操作数据库 List<User> users = mapper.selectUser(); for (User user: users){ System.out.println(user); } // 关闭 SqlSession session.close(); } }
在UserMapper中添加方法
public interface UserMapper { /** *功能描述: 查询用户集合 * @return java.util.List<com.shida.entity.User > * @author jiaoqianjin * @date 2021/3/3 */ List<User> selectUser(); /** *功能描述: 通过id查询用户 * @param id 用户id * @return com.shida.entity.User * @author jiaoqianjin * @date 2021/3/3 */ User selectUserById(int id); }
在UserMapper.xml中添加Select语句
<!-- [id]:statement的id,要求在命名空间内唯一 [parameterType]:入参的java类型 [resultType]:查询出的单条结果集对应的java类型 [#{}]: 表示一个占位符? [#{id}]:表示该占位符待接收参数的名称为id。注意:如果参数为简单类型时,#{}里面的参数名称可以是任意定义 --> <select id="selectUserById" parameterType="int" resultType="com.shida.entity.User"> SELECT * FROM USER WHERE id = #{id} </select>
在测试类中添加
@Test public void tsetSelectUserById() { SqlSession session = MybatisUtil.getSession(); //获取SqlSession连接 UserMapper mapper = session.getMapper(UserMapper.class); User user = mapper.selectUserById(1); System.out.println(user); session.close(); }
运行结果
在UserMapper中添加
/** *功能描述: 根据多个参数查询用户 * @param name 用户名 * @param phone 手机号 * @return com.shida.entity.User * @author jiaoqianjin * @date 2021/3/4 */ User selectUserByParams(String name, String phone);
在UserMapper.xml中添加
<select id="selectUserByParams" resultType="com.shida.entity.User"> SELECT * FROM USER WHERE name = #{name} and phone = #{phone} </select>
测试类
@Test public void testSelectUserByParams() { SqlSession session = MybatisUtil.getSession(); //获取SqlSession连接 UserMapper mapper = session.getMapper(UserMapper.class); String name = "王五"; String phone = "1536537158"; User user = mapper.selectUserByParams(name, phone); System.out.println(user); session.close(); }
运行结果
报错原因
因为java没有保存形参的记录,java在运行的时候会把 selectUserByParams(name, phone) 中的参数变成这样selectUserByParams(int arg0,int arg1),这样我们就没有办法去传递多个参数。
解决办法一:
在参数前添加**@Param**注解 mybatis提供了@Param这个注解来完成命名传入参数
/** * 功能描述: 根据多个参数查询用户 * * @param name 用户名 * @param phone 手机号 * @return com.shida.entity.User * @author jiaoqianjin * @date 2021/3/4 */ User selectUserByParams(@Param("name") String name, @Param("phone") String phone);
解决办法二:
使用Map
User selectUserByParams2(Map<String,String> map); ------- <select id="selectUserByParams2" parameterType="map" resultType="com.shida.entity.User"> SELECT * FROM USER WHERE name = #{name} and phone = #{phone} </select> -------- Map<String,String> map = new HashMap<String, String>(); map.put("name","王五"); map.put("phone","1536537158"); User user = mapper.selectUserByParams2(map);
两种方法对比:
Map方法缺点就在于可读性差,每次必须阅读他的键,才能明白其中的作用,后期维护困难。
@Param,可读性比较强
当参数为2-5个时候,用@param最佳,当大于5的时候,肯定会选择map了
Map替代方法:
使用BO传输
在entity中创建UserBo
public class UserBO { private String name; private String phone; //构造,有参,无参 //set/get //toString() }
UserMapper
User selectUserByParams3(UserBO userBO);
UserMapper
<select id="selectUserByParams3" parameterType="com.shida.entity.UserBO" resultType="com.shida.entity.User"> SELECT * FROM USER WHERE name = #{name} and phone = #{phone} </select>
测试类
@Test public void testSelectUserByParams3() { SqlSession session = MybatisUtil.getSession(); //获取SqlSession连接 UserMapper mapper = session.getMapper(UserMapper.class); UserBO userDTO = new UserBO(); userDTO.setName("王五"); userDTO.setPhone("1536537158"); User user = mapper.selectUserByParams3(userDTO); System.out.println(user); session.close(); }
一篇文章讲清楚VO,BO,PO,DO,DTO的区别
在UserMapper中添加
/** *功能描述: 根据name 模糊查询 * @param name 用户姓名 * @return com.shida.entity.User * @author jiaoqianjin * @date 2021/3/3 */ User selectUserLikeByName(String name);
在UserMapper.xml中添加Select语句
<!-- [${}]:表示拼接SQL字符串 [${value}]:表示要拼接的是简单类型参数。 注意: 1、如果参数为简单类型时,${}里面的参数名称必须为value 2、${}会引起SQL注入,一般情况下不推荐使用。但是有些场景必须使用${},比如order by ${colname} --> <select id="selectUserLikeByName" parameterType="String" resultType="com.shida.entity.User"> select * from USER where name like '%${value}%' </select>
在测试类中添加
@Test public void testSelectUserLikeByName() { SqlSession session = MybatisUtil.getSession(); //获取SqlSession连接 UserMapper mapper = session.getMapper(UserMapper.class); String name = "李"; User user = mapper.selectUserLikeByName(name); System.out.println(user); session.close(); }
**#{}**和${}
#{}:相当于预处理中的占位符?。
#{}里面的参数表示接收java输入参数的名称。
#{}可以接受HashMap、POJO类型的参数。
当接受简单类型的参数时,#{}里面可以是value,也可以是其他。
#{}可以防止SQL注入。
${}:相当于拼接SQL串,对传入的值不做任何解释的原样输出。
${}会引起SQL注入,所以要谨慎使用。
${}可以接受HashMap、POJO类型的参数。
当接受简单类型的参数时,${}里面只能是value。
在UserMapper中添加
/** *功能描述: 新增一个用户 * @param user 用户信息 * @return / * @author jiaoqianjin * @date 2021/3/3 */ int addUser(User user);
在UserMapper.xml中添加insert语句
<insert id="addUser" parameterType="com.shida.entity.User"> insert into user (id,name,phone) values (#{id},#{name},#{phone}) </insert>
如果主键值是通过MySQL自增机制生成的,此处可以不再显式的为id赋值
<insert id="addUser" parameterType="com.shida.entity.User"> insert into user (name,phone) values (#{name},#{phone}) </insert>
在测试类中添加
@Test public void testAddUser() { SqlSession session = MybatisUtil.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); User user = new User(4,"周六","15936537150"); int i = mapper.addUser(user); System.out.println(i); //提交事务,重点!不写的话不会提交到数据库 session.commit(); session.close(); }
在UserMapper中添加
/** *功能描述: 用户修改 * @param user 待修改的用户信息 * @return int * @author jiaoqianjin * @date 2021/3/3 */ int updateUser(User user);
在UserMapper.xml中添加update语句
<update id="updateUser" parameterType="com.shida.entity.User"> update user set name=#{name},phone=#{phone} where id = #{id} </update>
在测试类中添加
@Test public void testUpdateUser() { SqlSession session = MybatisUtil.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); User user = new User(); user.setId(1); user.setName("李思思"); user.setPhone("12345678910"); int i = mapper.updateUser(user); System.out.println(i); // 提交事务 session.commit(); session.close(); }
在UserMapper中添加
/** *功能描述: 删除用户 * @param id 待删除用户id * @return int * @author jiaoqianjin * @date 2021/3/3 */ int deleteUser(int id);
在UserMapper.xml中添加delete语句
<delete id="deleteUser" parameterType="int"> delete from user where id = #{id} </delete>
在测试类中添加
@Test public void testDeleteUser() { SqlSession session = MybatisUtil.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); int i = mapper.deleteUser(5); System.out.println(i); //提交事务,重点!不写的话不会提交到数据库 session.commit(); session.close(); }
<?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"> <configuration> <!-- 配置mybatis的环境信息 --> <environments default="development"> <environment id="development"> <!-- 配置JDBC事务控制,由mybatis进行管理 --> <transactionManager type="JDBC"/> <!-- 配置数据源,采用传统的javax. sql. DataSource规范中的连接池 --> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis_01?useSSL=false&useUnicode=true&characterEncoding=utf8"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <!-- 映射器 : 定义映射SQL语句文件 --> <mappers> <mapper resource="com/shida/dao/UserMapper.xml"/> </mappers> </configuration>
dataSource 有三种内建的数据源类型
type="[UNPOOLED|POOLED|JNDI]"
的用法
<!-- 1. 使用相对于类路径的资源引用 --> <mappers> <mapper resource="com/shida/dao/UserMapper.xml"/> </mappers> <!-- 不使用 2. 使用完全限定路径 --> <mappers> <mapper url="F:\teach\project\mybatis-01\src\main\java\com\shida\dao\UserMapper.xml"/> </mappers> <!-- 3. 使用映射器接口实现类的完全限定类名 需要mapper接口和mapper映射文件称相同,且放到同一个目录下 --> <mappers> <mapper class="com.shida.dao.UserMapper"/> </mappers> <!-- 推荐使用 4. 将包内的映射器接口实现全部注册为映射器 需要mapper接口和mapper映射文件称相同,且放到同一个目录下 --> <mappers> <package name="com.shida.dao"/> </mappers>
<?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="com.shida.mapper.UserMapper"> </mapper>
当namespace绑定接口后,不用写接口实现类,mybatis会通过该绑定自动帮你找到对应要执行的SQL语句
namespace中文意思:命名空间:
namespace的命名必须跟某个接口同名
接口中的方法与映射文件中sql语句id应该一一对应
<select id="selectUser" resultType="com.shida.entity.User"> select * from user </select>
namespace命名规则 : 包名+类名
properties数据库文件配置,设置为外部配置可替换
新建一个db.properties文件
driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/mybatis_01?useSSL=false&useUnicode=true&characterEncoding=utf8 username=root password=123456
修改mybatis-config.xml
别名是使用是为了在映射文件中,更方便的去指定参数和结果集的类型,不再用写很长的一段全限定名。
对于以往的开发过程,我们会经常使用到debug模式来调节,跟踪我们的代码执行过程。但是现在使用Mybatis是基于接口,配置文件的源代码执行过程。因此,我们必须选择日志工具来作为我们开发,调节程序的工具。
使用步骤:
1、导入log4j的包
<dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
2、配置文件编写
日志级别在开发阶段设置成DEBUG,在生产阶段设置成INFO或者ERROR。
Log4j建议只使用四个级别,优先级从高到低分别是 ERROR、WARN、INFO、DEBUG。通过在这里定义的级别,您可以控制到应用程序中相应级别的日志信息的开关。比如在这里定义了INFO级别,则应用程序中所有DEBUG级别的日志信息将不被打印出来,也是说大于等于的级别的日志才输出
# 全局日志配置 log4j.rootLogger=WARN, stdout # MyBatis 日志配置,打印一组映射器的日志,只需要打开映射器所在的包的日志功能即可 log4j.logger.com.shida.dao.UserMapper=TRACE # 控制台输出 log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
3、setting设置日志实现
<settings> <setting name="logImpl" value="LOG4J"/> </settings>
4 在程序中使用Log4j进行输出!
public class UserTest { static Logger logger = Logger.getLogger(UserTest.class); @Test public void selectUser() { logger.info("info:进入selectUser方法"); logger.debug("debug:进入selectUser方法"); logger.error("error: 进入selectUser方法"); SqlSession session = MybatisUtil.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); List<User> users = mapper.selectUser(); for (User user : users) { System.out.println(user); } session.close(); } }
解决实体类属性名与数据库表的字段名不一致
场景还原
创建一个User1实体类
public class User1 { private int id; private String name; private String number; // 手机号与数据库中的不一样 //构造方法 //set/get //toString() }
创建一个接口
/** * 功能描述: 通过id查询用户 * * @param id 用户id * @return com.shida.module.User * @author jiaoqianjin * @date 2021/3/3 */ User1 selectUser1ById(int id);
创建一个查询语句
<select id="selectUser1ById" parameterType="int" resultType="com.shida.entity.User1"> SELECT * FROM USER WHERE id = #{id} </select>
测试
@Test public void testSelectUser1ById() { SqlSession session = MybatisUtil.getSession(); //获取SqlSession连接 UserMapper mapper = session.getMapper(UserMapper.class); User1 user = mapper.selectUser1ById(1); System.out.println(user); session.close(); }
运行结果
分析:
select * from user where id = #{id} 可以看做
select id,name,phone from user where id = #{id}
mybatis会根据这些查询的列名(会将列名转化为小写,数据库不区分大小写) , 去对应的实体类中查找相应列名的set方法设值 , 由于找不到setPhone() , 所以number返回null ; 【自动映射】
解决方案
<select id="selectUser1ById" parameterType="int" resultType="com.shida.entity.User1"> select id, name, phone as number from user where id = #{id} </select>
<resultMap id="UserMap" type="com.shida.entity.User1"> <!-- id为主键 --> <id column="id" property="id"/> <!-- column是数据库表的列名 , property是对应实体类的属性名 --> <result column="name" property="name"/> <result column="phone" property="number"/> </resultMap> <select id="selectUser1ById" parameterType="int" resultMap="UserMap"> SELECT * FROM USER WHERE id = #{id} </select>
https://zhuanlan.zhihu.com/p/146659383
1、IDEA安装Lombok插件
2、引入Maven依赖
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.10</version> </dependency>
3、在代码中增加注解
@Data //GET,SET,ToString,有参,无参构造 public class Teacher { private int id; private String name; }
/** * 功能描述: 查询用户集合 * * @return java.util.List<com.shida.module.User> * @author jiaoqianjin * @date 2021/3/3 */ @Select("select * from user") List<User> selectUser();
在mybatis的核心配置文件中注入
<!--使用class绑定接口--> <mappers> <mapper class="com.shida.dao"/> </mappers>
测试
@Test public void selectUser() { SqlSession session = MybatisUtil.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); List<User> users = mapper.selectUser(); for (User user : users) { System.out.println(user); } session.close(); }
改造MybatisUtils工具类的getSession( ) 方法,重载实现。
public static SqlSession getSession(){ //事务自动提交 return sqlSessionFactory.openSession(true); }
编写接口新增方法
//添加一个用户 @Insert("insert into user (id,name,phone) values (#{id},#{name},#{phone})") int addUser(User user);
测试
@Test public void testAddUser() { SqlSession session = MybatisUtil.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); User user = new User(5, "周六", "15936537150"); int i = mapper.addUser(user); System.out.println(i); session.close(); }
编写删除接口
@Delete("delete from user where id = #{id}") int deleteUser(@Param("id")int id);
测试
@Test public void testDeleteUser() { SqlSession session = MybatisUtil.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); int i = mapper.deleteUser(5); System.out.println(i); session.close(); }
编写修改接口
@Update("update user set name=#{name},phone=#{phone} where id = #{id}") int updateUser(User user);
测试
@Test public void testUpdateUser() { SqlSession session = MybatisUtil.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); User user = new User(); user.setId(1); user.setName("李2思"); user.setPhone("12345678910"); int i = mapper.updateUser(user); System.out.println(i); session.close(); }