对PreparedStatement的使用的插入案例进行封装如下
JDBCUtils.java
package com.atmf;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.util.Properties;
import org.junit.Test;
public class JDBCUtils {
/**
* @Describe 获取数据库的连接
* @auther mofei
* @date 2020年11月3日下午6:25:54
*/
public static Connection getConnection() throws Exception{
//1,加载配置文件
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");
Properties pr = new Properties();
pr.load(is);
//2,读取配置信息
String user = pr.getProperty("user");
String password = pr.getProperty("password");
String url = pr.getProperty("url");
String driverClass = pr.getProperty("driverClass");
//3.加载驱动
Class.forName(driverClass);
//4,获取连接
Connection con = DriverManager.getConnection(url, user,password);
return con;
}
//增删改资源的关闭
public static void closeReource(Connection con, PreparedStatement ps){
try {
if(ps != null)
ps.close();
} catch (Exception e) {
e.printStackTrace();
}
try {
if(con != null)
con.close();
} catch (Exception e) {
e.printStackTrace();
}
}
//查询资源的关闭
public static void closeResource(java.sql.Connection con, PreparedStatement ps, ResultSet rs){
try {
if(con != null)
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(ps != null)
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(rs != null)
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
对表进行增删改操作
修改
package com.atmf;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.junit.Test;
public class PreparedStatementUpdateTest {
@Test
public void testUpdate() {
Connection con = null;
PreparedStatement ps = null;
try {
//1,获取数据库的连接
con = JDBCUtils.getConnection();
//2,预编译sql语句,返回PreparedStatement的实例
String sql = "update customers set name = ? where id = ?";
ps = con.prepareStatement(sql);
//3,填充占位符
ps.setObject(1, "提莫");
ps.setObject(2, 1);
//4,执行
ps.execute();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
//5,资源的关闭
JDBCUtils.closeReource(con, ps);
}
}
}
结果:
插入
package com.atmf;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.junit.Test;
public class PreparedStatementUpdateTest {
@Test
public void testUpdate() {
Connection con = null;
PreparedStatement ps = null;
try {
//1,获取数据库的连接
con = JDBCUtils.getConnection();
//2,预编译sql语句,返回PreparedStatement的实例
String sql = "insert into customers(name) value(?)";
ps = con.prepareStatement(sql);
//3,填充占位符
ps.setObject(1, "王者农药");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date date = sdf.parse("1995-11-03");
ps.setDate(2, new java.sql.Date(date.getTime()));
//4,执行
ps.execute();
} catch (SQLException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally {
//5,资源的关闭
JDBCUtils.closeReource(con, ps);
}
}
}
删除
package com.atmf;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.junit.Test;
public class PreparedStatementUpdateTest {
@Test
public void testUpdate() {
Connection con = null;
PreparedStatement ps = null;
try {
//1,获取数据库的连接
con = JDBCUtils.getConnection();
//2,预编译sql语句,返回PreparedStatement的实例
String sql = "delete from customers where id = ?";
ps = con.prepareStatement(sql);
//3,填充占位符
ps.setObject(1, 3);
//4,执行
ps.execute();
} catch (SQLException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally {
//5,资源的关闭
JDBCUtils.closeReource(con, ps);
}
}
}
以上,对表的增删改操作之间的不同就是SQL语句的不同,将其进行封装从而实现对同一数据库中表的增删改操作的通用,通过调用update()方法就能完成,代码如下所示。
通用的增删改操作
public void update(String sql, Object ...args) {
Connection con =null;
PreparedStatement ps = null;
try {
//1,获取数据库的连接
con = JDBCUtils.getConnection();
//2,预编译sql语句,返回PreparedStatement的实例
ps = con.prepareStatement(sql);
//3,填充占位符
for(int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
//4,执行
ps.execute();
} catch (Exception e) {
e.printStackTrace();
} finally {
//5,资源的关闭
JDBCUtils.closeReource(con, ps);
}
}
对表进行查询操作
ORM编程思想
对象关系映射(英语:Object Relational Mapping,简称ORM,或O/RM,或O/R mapping),是一种程序设计技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换。从效果上说,它其实是创建了一个可在编程语言里使用的“虚拟对象数据库”。
对象数据库是一种以对象形式表示信息的数据库。对象数据库的数据库管理系统被称为ODBMS或OODBMS。
两个主要原因让用户使用对象数据库技术。首先,关系数据库在管理复杂数据时显得笨重。其次,被应用软件操作的数据一般是用面向对象的编程语言如C++,Java,Delphi和C#写成,而那些用来转化数据表示和关系数据库元组的代码很冗繁,执行时也有不少耗时。这种应用程序和数据库表示信息的模式之间的不匹配有时也被称为impedance mismatch。
简单来讲:一个数据表对应一个java类,表中的一条记录对应java类的一个对象,表中的一个字段对应java类的一个属性
Java与SQL对应数据类型转换表
Java类型 | SQL类型 |
---|---|
boolean | BIT |
byte | TINYINT |
short | SMALLINT |
int | INTEGER |
long | BIGINT |
String | CHAR,VARCHAR,LONGVARCHAR |
byte array | BINARY , VAR BINARY |
java.sql.Date | DATE |
java.sql.Time | TIME |
java.sql.Timestamp | TIMESTAMP |
ResultSetMetaData
-
可用于获取关于 ResultSet 对象中列的类型和属性信息的对象
-
ResultSetMetaData rsmd = rs.getMetaData();
-
getColumnName(int column):获取指定列的名称
-
getColumnLabel(int column):获取指定列的别名
-
getColumnCount():返回当前 ResultSet 对象中的列数。
-
getColumnTypeName(int column):检索指定列的数据库特定的类型名称。
-
getColumnDisplaySize(int column):指示指定列的最大标准宽度,以字符为单位。
-
isNullable(int column):指示指定列中的值是否可以为 null。
-
isAutoIncrement(int column):指示是否自动为指定列进行编号,这样这些列仍然是只读的。
-
第一个查询操作
第一步:建立数据库及表
CREATE DATABASE test;
USE test;
CREATE TABLE customers(
`id` INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
`name` VARCHAR(15) DEFAULT NULL,
`email` VARCHAR(20) DEFAULT NULL,
`birth` DATE DEFAULT NULL,
`photo` MEDIUMBLOB DEFAULT NULL
);
INSERT INTO customers(`name`,`email`,`birth`)
VALUES('君莫笑','yege@qzgs.com','2035-05-29'),
('苏沐橙','mucheng@)qzgs.com','2042-02-18'),
('黄少天','hualao@qzgs.com','2038-06-12');
第二步:创建一个数据库对象映射的java类
public class Customer {
private int id;
private String name;
private String email;
private Date birth;
public Customer() {
}
public Customer(int id, String name, String email, Date birth) {
this.id = id;
this.name = name;
this.email = email;
this.birth = birth;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Date getBirth() {
return birth;
}
public void setBirth(Date birth) {
this.birth = birth;
}
@Override
public String toString() {
return "Customer{" +
"id=" + id +
", name='" + name + '\'' +
", email='" + email + '\'' +
", birth=" + birth +
'}';
}
}
第三步:创建配置文件
user=root
password=123456
url=jdbc:mysql://localhost:3306/test
driverClass=com.mysql.jdbc.Driver
第四步:案例测试
@Test
public void queryTest() {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
//建立连接
conn = JDBCUtils.getConnection();
//预编译sql语句
String sql = "select id,name,email,birth from customers where id = ?";
ps = conn.prepareStatement(sql);
//填充占位符
ps.setInt(1,1);
//执行,并返回结果集
rs = ps.executeQuery();
//处理结果集
if(rs.next()){
//获取当前这条数据的各个字段值
int id = rs.getInt(1);
String name = rs.getString(2);
String email = rs.getString(3);
Date birth = rs.getDate(4);
System.out.println(new Customer(id,name,email,birth));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭资源(关闭conn连接,ps,rs)
// ps.close();//立即释放这个语句对象的数据库和JDBC资源,而不是等待它自动关闭时发生。通常好的做法是在使用完资源后立即释放它们,以避免占用数据库资源。
// conn.close();//立即释放这个连接对象的数据库和JDBC资源,而不是等待它们被自动释放
// rs.close();//立即释放这个ResultSet对象的数据库和JDBC资源,而不是在自动关闭时等待该操作发生。
//将上面的是三个关闭资源写入JDBCUtils.java文件(本篇开头那个文件)中直接调用
JDBCUtils.closeResource(conn,ps,rs);
}
}
第五步:控制台打印结果显示
Customer{id=1, name='君莫笑', email='yege@qzgs.com', birth=2035-05-29}
-------------------------------------------------------------------------------------------------------------------------
涉及到 ResultSet()—>结果集 与 ResultSetMetaData()—>结果集元数据及反射
针对于各自的表的通用查询
通用的查询操作1
常用的方法:
ResultSet
-
查询需要调用PreparedStatement 的 executeQuery() 方法,查询结果是一个ResultSet 对象
-
ResultSet 对象以逻辑表格的形式封装了执行数据库操作的结果集,ResultSet 接口由数据库厂商提供实现
-
ResultSet 返回的实际上就是一张数据表。有一个指针指向数据表的第一条记录的前面。
-
ResultSet 对象维护了一个指向当前数据行的游标,初始的时候,游标在第一行之前,可以通过 ResultSet 对象的 next() 方法移动到下一行。调用 next()方法检测下一行是否有效。若有效,该方法返回 true,且指针下移。相当于Iterator对象的 hasNext() 和 next() 方法的结合体。
-
当指针指向一行时, 可以通过调用 getXxx(int index) 或 getXxx(int columnName) 获取每一列的值。
- 例如: getInt(1), getString(“name”)
- 注意:Java与数据库交互涉及到的相关Java API中的索引都从1开始。
Object getObject(int columnIndex) throws SQLException;
用到的方法:
ResultSetMetaData
-
可用于获取关于 ResultSet 对象中列的类型和属性信息的对象
-
ResultSetMetaData rsmd = rs.getMetaData();
-
getColumnName(int column):获取指定列的名称
-
getColumnLabel(int column):获取指定列的别名
-
getColumnCount():返回当前 ResultSet 对象中的列数。
-
getColumnTypeName(int column):检索指定列的数据库特定的类型名称。
-
getColumnDisplaySize(int column):指示指定列的最大标准宽度,以字符为单位。
-
isNullable(int column):指示指定列中的值是否可以为 null。
-
isAutoIncrement(int column):指示是否自动为指定列进行编号,这样这些列仍然是只读的。
-
//通用查询操作
public Customer queryForCustomers(String sql,Object ...args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
//第一步:获取连接
conn = JDBCUtils.getConnection();
//第二步:预编译sql语句 将查询语句手动输入,args表示可变的占位符参数
ps = conn.prepareStatement(sql);
//第三步:填充占位符
for(int i = 0;i < args.length;i++){
ps.setObject(i + 1,args[i]);//占位符参数索引是从1开始的
}
//第四步:执行,并返回结果集
rs = ps.executeQuery();
//第五步:处理结果集
//如果有数据,就去取数据,但是取几次呢???这就需要结果集元数据(ResultSetMetaData getMetaData():检索此ResultSet对象的列的数量、类型和属性。)
ResultSetMetaData rsmd = rs.getMetaData();
//获取结果集的列数
int columnCount = rsmd.getColumnCount();
if(rs.next()){
Customer customer = new Customer();
//处理结果集一行数据中的每一个列
for(int i = 0;i < columnCount;i++){
//获得结果集上的列值
Object columnValue = rs.getObject(i + 1);
//获得值对应的列名字段
String columnName = rsmd.getColumnName(i + 1);
//通过反射给customer对象指定的columnClassName属性,赋值为columnValue
Field field = Customer.class.getDeclaredField(columnName);
field.setAccessible(true);
field.set(customer,columnValue);//第一个参数:要传入设置的对象,第二个参数:要传入实参
}
return customer;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//第六步:关闭资源
JDBCUtils.closeResource(conn,ps,rs);
}
//如果没有结果,返回null
return null;
}
测试:
@Test
public void queryForCustomersTest(){
String sql = "select id,name,birth,email from customers where id = ?";
Customer customer = queryForCustomers(sql, 2);
System.out.println(customer);
}
结果打印:
Customer{id=2, name='苏沐橙', email='mucheng@)qzgs.com', birth=2042-02-18}
通用的查询操作2
说明通用查询操作2与通用查询操作1就只有2行代码不同
1,sql字符串的书写不同
2,将获取每个列的列名:getColumnName() ———替换为————>获取每个列的别名:getColumnLabel()
3,在数据库字段名与java中属性名一致情况下,可以使用getColumnName(),反之,则会报错:没有这个字段
4,如果sql中没有给字段中起别名,getColumnLabel()获取的就是别名,所以不管有没有写别名都可以使用用getColumnLabel(尽量使用getColumnLabel)
将数据库test中customers表的字段名更改一下,以致表字段名与数据库对象映射的java类中的属性名不一致,或者另外创建一个表acg,表结构如下:
CREATE TABLE acg(
id INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
`acg_name` VARCHAR(15) DEFAULT NULL,
`acg_email` VARCHAR(20) DEFAULT NULL,
`acg_birth` DATE DEFAULT NULL,
`acg_photo` MEDIUMBLOB DEFAULT NULL
);
INSERT INTO acg(`acg_name`,`acg_email`,`acg_birth`)
VALUES('君莫笑','yege@qzgs.com','2035-05-29'),
('苏沐橙','mucheng@)qzgs.com','2042-02-18'),
('黄少天','hualao@qzgs.com','2038-06-12');
同时数据库对象映射的java类中的属性名也要更改,使用驼峰命名法:
public class Acg {
private int acgId;
private String acgName;
private String acgEmail;
private Date acgBirth;
...
其余保持不变。进行测试:
public Acg queryForCustomers2(String sql,Object ...args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
//获得连接
conn = JDBCUtils.getConnection();
//预编译sql语句
ps = conn.prepareStatement(sql);
//填充占位符
for(int i = 0;i < args.length;i++){
ps.setObject(i + 1,args[i]);
}
//执行,并返回结果集
rs = ps.executeQuery();
//获得结果集元数据
ResultSetMetaData rsmd = rs.getMetaData();
//获得结果集元数据的列数
int columnCount = rsmd.getColumnCount();
if(rs.next()){
Acg acg = new Acg();
for(int i = 0;i < columnCount;i++){
Object columnValue = rs.getObject(i + 1);
String columnLabel = rsmd.getColumnLabel(i + 1);
//通过反射给customer对象指定的columnClassName属性,赋值为columnValue
Field field = Acg.class.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(acg,columnValue);
}
return acg;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭资源
JDBCUtils.closeResource(conn,ps,rs);
}
return null;
}
测试:
@Test
public void queryForCustomersTest2() {
String sql = "select acg_id acgId,acg_name acgName,acg_email acgEmail,acg_birth acgBirth from acg where acg_id = ?";
Acg acg = queryForCustomers2(sql, 3);
System.out.println(acg);
}
结果打印:
Acg{acgId=3, acgName='黄少天', acgEmail='hualao@qzgs.com', acgBirth=2038-06-12}
-------------------------------------------------------------------------------------------------------------------------
针对于通用的表的通用查询
通用的表的查询操作,用于返回数据表中的一条记录
使用泛型方法
public class PreparedStatementTest {
public <T> T queryForTable(Class<T> clazz,String sql,Object ...args){
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
//获得连接
conn = JDBCUtils.getConnection();
//预编译sql语句
ps = conn.prepareStatement(sql);
//填充占位符
for(int i = 0;i < args.length;i++){
ps.setObject(i + 1,args[i]);
}
//执行,并返回结果集
rs = ps.executeQuery();
//获得结果集元数据
ResultSetMetaData rsmd = rs.getMetaData();
//获得结果集元数据的列数
int columnCount = rsmd.getColumnCount();
if(rs.next()){
T t = clazz.newInstance();
for(int i = 0;i < columnCount;i++){
Object columnValue = rs.getObject(i + 1);
String columnLabel = rsmd.getColumnLabel(i + 1);
//通过反射给customer对象指定的columnClassName属性,赋值为columnValue
Field field = clazz.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(t,columnValue);
}
return t;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭资源
JDBCUtils.closeResource(conn,ps,rs);
}
return null;
}
@Test
public void queryForTableTest(){
String sql1 = "select id,name,birth,email from customers where id = ?";
Customer customer = queryForTable(Customer.class,sql1, 1);
System.out.println(customer);
String sql2 = "select acg_id acgId,acg_name acgName,acg_email acgEmail,acg_birth acgBirth from acg where acg_id = ?";
Acg acg = queryForTable(Acg.class,sql2, 2);
System.out.println(acg);
}
}
测试结果:
Customer{id=1, name='君莫笑', email='yege@qzgs.com', birth=2035-05-29}
Acg{acgId=2, acgName='苏沐橙', acgEmail='mucheng@)qzgs.com', acgBirth=2042-02-18}
通用的表的查询操作,用于返回数据表中的多条记录
public class PreparedStatementTest {
public <T> List<T> queryForList(Class<T> clazz, String sql, Object ...args){
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
//获得连接
conn = JDBCUtils.getConnection();
//预编译sql语句
ps = conn.prepareStatement(sql);
//填充占位符
for(int i = 0;i < args.length;i++){
ps.setObject(i + 1,args[i]);
}
//执行,并返回结果集
rs = ps.executeQuery();
//获得结果集元数据
ResultSetMetaData rsmd = rs.getMetaData();
//获得结果集元数据的列数
int columnCount = rsmd.getColumnCount();
//创建一个集合,用于存储对象
ArrayList<T> list = new ArrayList<>();
while(rs.next()){
T t = clazz.newInstance();
for(int i = 0;i < columnCount;i++){
Object columnValue = rs.getObject(i + 1);
String columnLabel = rsmd.getColumnLabel(i + 1);
//通过反射给customer对象指定的columnClassName属性,赋值为columnValue
Field field = clazz.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(t,columnValue);
}
list.add(t);
}
return list;
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭资源
JDBCUtils.closeResource(conn,ps,rs);
}
return null;
}
@Test
public void queryForListTest(){
String sql2 = "select acg_id acgId,acg_name acgName,acg_email acgEmail,acg_birth acgBirth from acg where acg_id < ?";
List<Acg> list = queryForList(Acg.class, sql2, 4);
for(Acg acg : list){
System.out.println(acg);
}
}
}
测试结果:
Acg{acgId=1, acgName='君莫笑', acgEmail='yege@qzgs.com', acgBirth=2035-05-29}
Acg{acgId=2, acgName='苏沐橙', acgEmail='mucheng@)qzgs.com', acgBirth=2042-02-18}
Acg{acgId=3, acgName='黄少天', acgEmail='hualao@qzgs.com', acgBirth=2038-06-12}
操作BLOB类型字段
MySQL BLOB类型
- MySQL中,BLOB是一个二进制大型对象,是一个可以存储大量数据的容器,它能容纳不同大小的数据。
- 插入BLOB类型的数据必须使用PreparedStatement,因为BLOB类型的数据无法使用字符串拼接写的。
- MySQL的四种BLOB类型(除了在存储的最大信息量上不同外,他们是等同的)
MySQL有四种BLOB类型:
tinyblob:仅255个字符
blob:最大限制到65K字节
mediumblob:限制到16M字节
longblob:可达4GB
- 实际使用中根据需要存入的数据大小定义不同的BLOB类型。
- 需要注意的是:如果存储的文件过大,数据库的性能会下降。
- 如果在指定了相关的Blob类型以后,还报错:xxx too large,那么在mysql的安装目录下,找my.ini文件加上如下的配置参数: max_allowed_packet=16M。同时注意:修改了my.ini文件之后,需要重新启动mysql服务。
向数据表acg插入Blob类型的数据
public class BlobTest {
@Test
public void insertBlobTest() {
Connection conn = null;
PreparedStatement ps = null;
try {
conn = JDBCUtils.getConnection();
String sql = "insert into acg(`acg_name`,`acg_email`,`acg_birth`,`acg_photo`) values(?,?,?,?)";
ps = conn.prepareStatement(sql);
ps.setObject(1,"叶修");
ps.setObject(2,"yebuxiu@qzgs.com");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date date = sdf.parse("2035-05-29");
ps.setObject(3,date);
FileInputStream fis = new FileInputStream(new File("src\\叶修.gif"));
ps.setObject(4,fis);
ps.execute();
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn,ps);
}
}
}
执行结果:(找了个动图,它不会不动,hh)
将Blob类型字段的数据,下载下来保存到本地
public class BlobTest {
@Test
public void queryBlobTest() {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
InputStream is = null;
FileOutputStream fos = null;
try {
conn = JDBCUtils.getConnection();
String sql = "select `acg_id`,`acg_name`,`acg_email`,`acg_birth`,`acg_photo` from acg where `acg_id` = ?";
ps = conn.prepareStatement(sql);
ps.setInt(1,4);
rs = ps.executeQuery();
if(rs.next()){
int acgId = rs.getInt("acg_id");
String acgName = rs.getString("acg_name");
String acgEmail = rs.getString("acg_email");
Date acgBirth = rs.getDate("acg_birth");
Acg acg = new Acg(acgId, acgName, acgEmail, acgBirth);
System.out.println(acg);
//将blob类型的字段下载下来,以文件的方式保存到本地
Blob acgPhoto = rs.getBlob("acg_photo");
is = acgPhoto.getBinaryStream();
fos = new FileOutputStream(new File("src\\君不笑.gif"));
byte[] buffer = new byte[20];
int len;
while((len = is.read(buffer)) != -1){
fos.write(buffer,0,len);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if(fos != null)
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if(is != null)
is.close();
} catch (IOException e) {
e.printStackTrace();
}
JDBCUtils.closeResource(conn,ps,rs);
}
}
}
测试结果:
Acg{acgId=4, acgName='叶修', acgeMail='yebuxiu@qzgs.com', acgBirth=2035-05-29}