LocalDateTime

====问题1

java插入时

代码是这样的,插入LocalDateTime类型,在mysql中的datetime类型

java 复制代码
    public void set() {
        TestTime testTime = new TestTime();


        LocalDateTime localDateTime = LocalDateTime.now();
        testTime.setEventTime(localDateTime);

        testTimeService.save(testTime);
    }

日志是这样的

java 复制代码
JDBC Connection [HikariProxyConnection@1265966702 wrapping com.mysql.cj.jdbc.ConnectionImpl@1c5fea58] will not be managed by Spring
==>  Preparing: INSERT INTO test_time ( event_time, created_at ) VALUES ( ?, ? )
==> Parameters: 2026-07-31T10:14:47.511(LocalDateTime), null
<==    Updates: 1

但mysql插入会报错

java 复制代码
INSERT INTO test_time ( event_time, created_at ) VALUES ( 2026-07-31T10:24:47.511, null )

会报错

java 复制代码
1064 - 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 ':24:47.511, null )' at line 1

解析

你看到的日志信息其实是 MyBatis 在打印 SQL 参数时的 Java 对象显示形式,并不是真正发送到数据库的 SQL。下面分点说明:


1. 日志中的 2026-07-31T10:14:47.511 是什么?

java 复制代码
==> Parameters: 2026-07-31T10:14:47.511(LocalDateTime), null
  • 这是 MyBatis 调用 LocalDateTimetoString() 方法,将对象转成字符串用于调试展示。

  • 实际 JDBC 驱动在发给 MySQL 之前 ,会把 LocalDateTime 转换为符合 SQL 标准的格式,例如:

    java 复制代码
    '2026-07-31 10:14:47.511'

    并用 PreparedStatement.setTimestamp() 或类似方法传递,因此数据库能正确识别。

你手动执行失败,是因为你把 Java 对象的字符串表示直接写进了 SQL,既没有加引号,格式也不是 MySQL 的合法日期字面量。

正确的 MySQL 日期写法(带时间):

java 复制代码
INSERT INTO test_time (event_time, created_at) 
VALUES ('2026-07-31 10:24:47.511', NULL);

或者使用 TIMESTAMP 格式。


2. 为什么 created_at 传了 null 还能插入成功?

你的建表语句是:

java 复制代码
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
  • MySQL 5.6 及以上 ,如果 explicit_defaults_for_timestamp 是默认值(OFF),即使字段允许 NULL,当显式插入 NULL 时,MySQL 会自动替换成当前时间戳(相当于触发了默认值)。
  • explicit_defaults_for_timestamp=ON 时,插入 NULL 就会存储 NULL不会触发默认值
  • 日志显示 Updates: 1 说明插入成功了,但为了行为一致,最好避免插入 NULL

3. 如何让 MyBatis 不再插入 created_at 列?

既然 created_at 由数据库自动生成,插入时就应该省略这个字段 ,而不是传 null

方法一:INSERT 语句不写此字段

java 复制代码
<insert id="insertTestTime" parameterType="TestTime">
    INSERT INTO test_time (event_time) VALUES (#{eventTime})
</insert>

对应的实体类中可以保留 createdAt 字段用于查询,但插入时不涉及。

方法二:使用动态 SQL 忽略 null

java 复制代码
<insert id="insertTestTime" parameterType="TestTime">
    INSERT INTO test_time
    <trim prefix="(" suffix=")" suffixOverrides=",">
        <if test="eventTime != null">event_time,</if>
        <!-- 故意不写 created_at -->
    </trim>
    <trim prefix="VALUES (" suffix=")" suffixOverrides=",">
        <if test="eventTime != null">#{eventTime},</if>
    </trim>
</insert>

方法三:JPA 中已经配置 insertable = false

如果你之后又用了 JPA 注解,这个配置可以阻止字段写入 insert 语句。


总结

  • 日志里的 2026-07-31T10:14:47.511 只是 Java 对象的 toString() 显示,实际传给数据库的是合法的日期值。
  • 手动执行 SQL 需要把日期字符串用引号括起来,格式为 'YYYY-MM-DD HH:MM:SS'
  • 建议在插入语句中直接去掉 created_at ,让数据库默认值生效,避免因 NULL 导致的各种兼容性问题。

如果你用的是 MyBatis 的 XML 映射,可以分享一下你的 mapper 文件,我能帮你改成不插入 created_at 的写法。

====问题2

时间类型'字符串'

在数据库中直接插入

java 复制代码
INSERT INTO test_time (event_time, created_at) 
VALUES ('2026-07-31 10:24:47.511', NULL);

为什么是带引号,这个不是时间类型吗,如果加了引号不就是字符串类型了吗?

解析

你提出的问题非常关键:为什么日期时间类型的值在 SQL 里要加引号,加引号不就变成字符串了吗?

答案在于 SQL 的语法规则 ------ 在 SQL 语句中,日期和时间值需要用字符串字面量的形式来书写,然后数据库引擎会将其解析并转换成内部的日期时间类型存储。所以,加引号只是书写格式的要求,并不代表数据库把它当作文本存储。

让我们详细拆解一下:


1. SQL 中的数据类型 vs. 字面量表示

数据库的 是有明确类型的(如 DATETIMETIMESTAMP),但是 你在 SQL 语句里写的值 叫做"字面量"(literal)。不同数据类型的字面量有不同的书写规则:

  • 数值字面量 :直接写数字,例如 12312.5(不需要引号)
  • 字符串字面量 :用单引号括起来,例如 'hello'
  • 日期/时间字面量 :标准 SQL 规定,日期和时间值必须用 字符串的形式 表示,并由数据库自动解析为日期时间类型。也就是说,你需要写 '2026-07-31 10:24:47.511',而不是 2026-07-31 10:24:47.511

为什么不能不加引号?因为如果不加引号,SQL 解析器会把 2026-07-31 当作一个算术表达式:2026 减 7 减 31,结果是 1988,空格后面又出现 10:24:47.511 更是完全无法解析,所以会报语法错误。


2. 加引号会被当作字符串存储吗?

不会。

当执行 INSERT INTO test_time (event_time) VALUES ('2026-07-31 10:24:47.511') 时:

  1. 数据库看到单引号包裹的内容,知道这是一个字符串字面量
  2. 然后检查目标列 event_time 的类型是 DATETIME
  3. 数据库会进行 类型转换 ,将字符串 '2026-07-31 10:24:47.511' 解析成一个内部的日期时间值(通常是某种结构体或时间戳),然后存入磁盘。
  4. 存储后,列里的数据类型仍然是 DATETIME不是字符串。你后续查询或者用编程语言读取时,拿到的是真正的日期时间类型。

这就像 Java 里写:

java 复制代码
LocalDateTime dt = LocalDateTime.parse("2026-07-31T10:24:47.511");

"2026-07-31T10:24:47.511" 是一个字符串,但解析后赋给了 LocalDateTime 变量。这里 SQL 的引号就相当于 Java 代码中的双引号,它只代表字面量的书写形式。


3. MySQL 的宽松语法

MySQL 除了标准的要求外,还允许一些快捷写法,但都基于字符串格式:

  • 'YYYY-MM-DD HH:MM:SS' 是最标准、最推荐的方式。
  • 也可以用 'YYYYMMDDHHMMSS' 这样的紧凑格式(不推荐)。
  • 不加引号 直接写 20260731102447 可能会被当成整数隐式转换,但这不是标准做法且易出错。

无论如何,日期时间值在 SQL 中必须被引号包围,才能被正确解析。


4. 你的 MyBatis 日志中的 2026-07-31T10:14:47.511(LocalDateTime) 为什么没引号?

MyBatis 日志打印参数时,显示的是 Java 对象本身toString() 输出(LocalDateTime 默认格式带 T),这不是 最终发送到数据库的 SQL 语句。JDBC 驱动会使用 PreparedStatement.setObject()setTimestamp()LocalDateTime 对象传递给 MySQL 协议,后台会自动处理类型转换,完全不需要人工加引号。你如果直接复制日志中的参数值写进原始 SQL,当然会失败,因为那根本不是 SQL 字面量。


总结

  • SQL 语法要求:日期时间值必须写成字符串字面量形式(加单引号),否则会被误认为是表达式。
  • 存储仍然是日期时间类型:数据库会自动将引号内的字符串转换为内部日期时间类型,不会当字符串存。
  • MyBatis 日志中的 2026-07-31T10:14:47.511 是 Java 对象的展示,你在直接写 SQL 时一定要加上引号并使用 'YYYY-MM-DD HH:MM:SS' 格式。

希望这个解释能帮你理清"引号"与"数据类型"的关系!

====问题3

java中只能使用LocalDateTime类型吗

解析

Java 中处理 MySQL 的 DATETIME / TIMESTAMP 字段,绝不是只能使用 LocalDateTime,有多种类型可选,各有适用场景。下面逐一说明。


1. Java 中没有名为 DateTime 的标准类

Java 标准库中并没有一个叫做 DateTime 的类型(除非你用 Joda-Time 等第三方库)。

常见的日期时间 API 有两代:

类型 所在包 说明
java.time.LocalDateTime java.time (Java 8+) 推荐,表示不含时区的日期时间(年月日时分秒纳秒),完美映射 MySQL 的 DATETIME
java.time.LocalDate java.time 仅日期,映射 DATE 类型。
java.time.LocalTime java.time 仅时间,映射 TIME 类型。
java.util.Date java.util 老的日期时间类,含时区信息,映射 DATETIMETIMESTAMP,不推荐新项目使用。
java.sql.Timestamp java.sql (继承 java.util.Date) 专用于 JDBC,可精确到纳秒,映射 DATETIMETIMESTAMP,传统项目常用。
java.sql.Date java.sql (继承 java.util.Date) 仅日期,映射 DATE
java.sql.Time java.sql 仅时间,映射 TIME
String java.lang 字符串形式,如 "2026-07-31 10:24:47",但失去了数据库原生的日期运算功能,不推荐。

2. 各种类型的详细对比

2.1 LocalDateTime(✅ 推荐)

  • 优点 :现代 API,线程安全,语义清晰(不依赖时区),与 MySQL 的 DATETIME 完美匹配。
  • 缺点 :如果数据库字段是 TIMESTAMP(带时区),LocalDateTime 处理时区需要小心(见后文)。
  • MyBatis 支持 :MyBatis 3.4+ 内置了 LocalDateTimeTypeHandler,直接可用。
  • JPA/Hibernate 支持 :Hibernate 5+ 自动支持 LocalDateTime
java 复制代码
private LocalDateTime eventTime;

2.2 java.util.Date

  • 优点:所有 Java 版本都认识,老项目大量使用。
  • 缺点:时间对象可变、线程不安全,API 设计糟糕,语义上带时区但通常用来表示本地时间,容易造成混淆。
  • 映射 :JDBC 驱动会将 java.util.Date 转换成 SQL 的 TIMESTAMP(即时区感知),可能导致时区转换问题。
java 复制代码
private Date eventTime;   // 需要 import java.util.Date

2.3 java.sql.Timestamp

  • 优点 :精确到纳秒,直接对应 MySQL 的 DATETIMETIMESTAMP 类型,JDBC 原生支持。
  • 缺点 :继承自 java.util.Date,同样可变、线程不安全,时间处理比较笨重。
  • MyBatis :可能需要自定义类型处理器或设置 typeHandlers
java 复制代码
private Timestamp eventTime;

2.4 String

  • 优点:格式完全可控,不需要关心 Java 日期 API。
  • 缺点 :失去了数据库日期类型的所有好处(如排序、区间查询、日期函数),且容易格式不一致。必须手动保证插入的字符串格式是数据库能解析的(如 'YYYY-MM-DD HH:MM:SS')。
  • 应用场景:很少用,除非是只做原样存取、无日期逻辑的字段。
java 复制代码
private String eventTime;  // 例如 "2026-07-31 10:24:47"

3. 不同数据库类型的匹配建议

MySQL 类型 首选 Java 类型 次选 Java 类型 说明
DATETIME LocalDateTime java.sql.Timestamp / String 不带时区,直接保存字面日期时间。
TIMESTAMP LocalDateTimeInstant / ZonedDateTime java.sql.Timestamp / String MySQL 的 TIMESTAMP 存储的是 UTC 值,写入和读取时会根据当前会话时区转换。如果只想保存"墙上时间",可用 LocalDateTime(通常与 DATETIME 行为一致);如果需要精确的时间点,建议用 Instant 或带时区的类型。

举个 TIMESTAMP 的例子

你存入 '2026-07-31 10:00:00',如果数据库会话时区是 +08:00,实际存储的 UTC 值是 '2026-07-31 02:00:00'

当你用 LocalDateTime 读取时,驱动可能直接返回 '2026-07-31 10:00:00'(因为驱动进行了时区还原),看起来和写入一样;如果用 Instant 读取,你会得到 UTC 时刻。


4. 在 MyBatis 中如何选择和使用

  • 直接使用 LocalDateTime :MyBatis 3.4.5+ 会自动使用 LocalDateTimeTypeHandler,无需额外配置。
  • 使用 java.util.DateTimestamp:MyBatis 同样内置了对应的类型处理器。
  • 使用 String :MyBatis 会直接把它当成字符串传给 JDBC,这就要求你传入的字符串格式必须是 MySQL 能接受的(例如 'YYYY-MM-DD HH:MM:SS'),你需要在业务代码中负责格式化。

示例:实体类使用 LocalDateTime

java 复制代码
import java.time.LocalDateTime;

public class TestTime {
    private LocalDateTime eventTime;
    // created_at 可定义为 LocalDateTime 但 insertable=false
    private LocalDateTime createdAt;
    // getters/setters...
}

如果一定要用 String

java 复制代码
private String eventTime;  // 自己保证格式为 "2026-07-31 10:24:47"

然后在插入前把 LocalDateTime 格式化为字符串:

java 复制代码
testTime.setEventTime(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));

5. 总结

  • 推荐使用 java.time.LocalDateTime :它干净、安全、语义匹配 MySQL 的 DATETIME 和大多数情况下的 TIMESTAMP
  • 老项目可能用 java.util.Datejava.sql.Timestamp,能运行但不够优雅。
  • 绝对不推荐用 String 存储日期时间,除非你有非技术原因(如接口强制要求)。
  • 选择类型时,一定要搞清楚你的业务逻辑是否需要时区信息,从而确定用 LocalDateTimeInstant 还是 ZonedDateTime

最后,不论你选择哪种 Java 类型,SQL 中写入的字面量 始终是带单引号的字符串格式(如 '2026-07-31 10:24:47'),而 JDBC 驱动会完成 Java 对象到 SQL 格式的安全转换,你不用担心。

====问题4

date能对应datetime吗

java中的date类型能对应上mysql的datetime吗?

解析

可以映射,但不完美。

java.util.Date 在 JDBC 规范中对应的是 java.sql.Timestamp(继承自 Date),而 Timestamp 的 JDBC 类型是 TIMESTAMP。MySQL 既有 DATETIME 又有 TIMESTAMP,两者虽然相似,但对时区处理不同。因此 java.util.Date 映射到 MySQL 的 DATETIME 列时,可能会出现时区转换导致的意外行为。


1. JDBC 类型映射关系

Java 类型 JDBC 类型 (java.sql.Types) 常用 MySQL 列类型
java.util.Date TIMESTAMP TIMESTAMPDATETIME
java.sql.Date DATE DATE
java.sql.Time TIME TIME
java.sql.Timestamp TIMESTAMP TIMESTAMPDATETIME

当你的实体字段是 java.util.Date,JDBC 驱动会将它视为 TIMESTAMP 类型的值发送给 MySQL。MySQL 收到一个 TIMESTAMP 值后,会根据目标列的类型进行转换:

  • 如果列是 TIMESTAMP:直接存储(带时区转换为 UTC 存储,读取时按当前会话时区转回)。
  • 如果列是 DATETIME:MySQL 会把传来的 TIMESTAMP当作是当前时区下的日期时间,然后原样存入(不做 UTC 转换)。

2. 潜在问题:时区陷阱

假设你的 MySQL 服务器时区为 +08:00(北京时间)。

插入

Java 代码创建一个 Date 对象,代表 2026-07-31 10:00:00 UTC+8(即 UTC 02:00:00)。

JDBC 驱动发送给 MySQL 的 TIMESTAMP 值是 '2026-07-31 10:00:00',同时携带着"这是数据库服务器时区下的时间"的语义。

  • 若列是 DATETIME:MySQL 直接存储字面值 '2026-07-31 10:00:00',不关心时区。
  • 若列是 TIMESTAMP:MySQL 先将其视作服务器时区(+08:00)的时间,转换为 UTC '2026-07-31 02:00:00' 存储。

读取

你用 java.util.Date 接收:

  • DATETIME 读取:驱动拿到 '2026-07-31 10:00:00',会理解为 UTC 时间 还是 本地时间 ?这取决于 JDBC 连接的 serverTimezone 参数。
    • 若设置 serverTimezone=Asia/Shanghai,驱动可能把字面值当作 UTC+8 来转换,最终 Java 拿到的 Date 可能是 2026-07-31 10:00:00 UTC+8(即正确的)。
    • 若未设置或设为 UTC,驱动可能直接把 '2026-07-31 10:00:00' 当成 UTC,结果你拿到的 Date 就错位了 8 小时。
  • TIMESTAMP 读取:MySQL 会自动转回会话时区的时间,驱动再转换为 Date,通常能得到正确的时间点。

结论java.util.Date 映射 DATETIME 时,由于 DATETIME 本身不带时区,驱动对字面值的解释依赖于连接参数,容易导致时区偏移。这种不确定性使得 Date 用于 DATETIME 并不是最佳选择。


3. 到底能不能用?怎么用?

能用,但需满足以下条件:

  • 你的应用不关心时区,只关心表面的日期时间数字(如"2026年7月31日10:00"),并且数据库连接时区与业务时区一致。
  • 确保 了 JDBC 连接参数 serverTimezone 设置正确(例如 ?serverTimezone=Asia/Shanghai),使得驱动将 DATETIME 的字面值解释为正确时区。
  • 你接受 java.util.Date 的缺陷(可变、线程不安全等),或者暂时无法使用 java.time

要避免的问题:

  • 永远不要依赖默认时区。
  • 如果你用 MySQL 的 DATETIME 存储未来时间(如预约时间),建议改用 LocalDateTime,因为 DATETIME 不涉及时区,LocalDateTime 语义完美匹配,没有歧义。

4. 推荐方案

列类型 推荐 Java 类型 说明
DATETIME java.time.LocalDateTime 语义最匹配,无时区问题。
TIMESTAMP java.time.InstantZonedDateTime 能准确表达时间点,时区正确处理。
老项目仍用 java.util.Date(必须用的话) 明确列类型为 TIMESTAMP 更稳定。

如果要继续使用 java.util.Date 映射 DATETIME,建议在 JDBC URL 中添加 serverTimezone 参数,并保持应用与数据库的时区一致。但长期来看,迁移到 java.time 是更可靠的做法。


简短回答你的问题java.util.Date 能够插入到 MySQL 的 DATETIME 列,JDBC 会自动处理,但由于 Date 携带时区信息而 DATETIME 列不存储时区,可能导致时区转换错误,除非你非常清楚自己的时区配置。因此,能用,但不推荐 ,最好使用 LocalDateTime

====问题5

返回带T

【mysql数据库字段类型是datetime,在java中是LocalDateTime】

数据库保存的是值是:2026-07-20 14:01:50

在java中查时的日志是:2026-07-31 10:14:48

java 复制代码
JDBC Connection [HikariProxyConnection@1337175056 wrapping com.mysql.cj.jdbc.ConnectionImpl@2de6966b] will not be managed by Spring
==>  Preparing: SELECT event_time,created_at FROM test_time
==> Parameters: 
<==    Columns: event_time, created_at
<==        Row: 2026-07-31 10:14:48, null
<==        Row: 2026-07-31 10:24:48, null
<==      Total: 2

但是返回给前端时是:

java 复制代码
"eventTime": "2026-07-31T10:14:48"

解析

你看到的现象完全正常:数据库中存储的是 2026-07-31 10:14:48(没有 T),查询也正确返回了这个字面值;但是 Jackson 在把 Java 的 LocalDateTime 对象序列化成 JSON 时,默认使用了 ISO‑8601 格式,即 2026-07-31T10:14:48

这是因为:

  • MySQL 的 DATETIME 列本身不包含时区,JDBC 驱动将数据库返回的 '2026-07-31 10:14:48' 转换成了 LocalDateTime 对象(日期和时间之间没有 T,这只是对象的内部表示)。
  • 当你通过 Spring Boot 返回这个实体时,ObjectMapper(Jackson)将 LocalDateTime 序列化为标准的 ISO‑8601 字符串,格式为 yyyy-MM-dd'T'HH:mm:ss(部分秒被省略)。
  • 这种格式是国际标准,大多数现代前端框架(如 Vue、React、Angular)都能直接解析,所以一般情况下不需要去掉 T

如果你仍然需要返回不带 T 的字符串(如 2026-07-31 10:14:48

你有三种方式可以自定义 JSON 输出格式:

方案一:在实体字段上使用 @JsonFormat(最灵活)

java 复制代码
import com.fasterxml.jackson.annotation.JsonFormat;
import java.time.LocalDateTime;

public class TestTime {
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime eventTime;

    // created_at 同理
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createdAt;

    // getters/setters...
}

这样,该字段序列化时就会变成 "2026-07-31 10:14:48",没有 T

⚠️ 反序列化 (前端传回 JSON 解析成 LocalDateTime)也会应用同样的格式,所以前端也必须传 "yyyy-MM-dd HH:mm:ss" 格式的字符串。

方案二:全局配置 Jackson 的 LocalDateTime 格式

application.ymlapplication.properties 中设置:

java 复制代码
spring:
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss

或者在 Java 配置类中定义一个 ObjectMapper bean,但这种方式会让所有 LocalDateTime 都统一格式,灵活性稍差。

方案三:手动格式化为 String 字段(不推荐)

你可以在实体中增加一个 String 类型字段,并在 getter 中手动格式化:

java 复制代码
public String getEventTimeFormatted() {
    return eventTime == null ? null : eventTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}

然后在返回的 JSON 中暴露 eventTimeFormatted 而不是 eventTime。但这会让实体变得混乱,不建议。


要不要去掉 T

  • 如果前后端分离,前端能够自然处理 ISO‑8601(T 分隔),建议保留,因为这是标准做法。
  • 如果必须兼容老系统或某些特定需求(如模板引擎直接展示字符串),可以按方案一或二修改。

补充说明

  • 你的 created_at 字段在插入时为 null,结果查询出来也是 null,说明数据库默认值没有生效(参考之前的讨论,可能因为显式传了 null 导致没触发 DEFAULT CURRENT_TIMESTAMP)。如果需要这个字段自动填充时间,请确保 MyBatis/JPA 的 INSERT 语句不包含 created_at 列。
  • 日志 Row: 2026-07-31 10:14:48, null 是 MyBatis 调用 LocalDateTime.toString() 的结果,toString() 输出也是 2026-07-31T10:14:48,但 MyBatis 日志出于美观做了简化显示,不代表实际值。实际 LocalDateTime 对象没有 T 的概念,T 只存在于序列化字符串中。

根据你的需要选择是否修改序列化格式即可。

相关推荐
fengci.8 小时前
Microweber CMS 未授权路径穿越漏洞(CVE-2026-65694)
android·开发语言·前端·学习·php
stevenzqzq9 小时前
Android 矢量图(Vector Drawable)核心坐标与尺寸机制技术文档
android
plainGeekDev10 小时前
运行时获取依赖 → 编译时注入
android·java·kotlin
Wiktok10 小时前
Vue3(Vite)打包安卓APP 完整最佳实践
android·vue
Co_Hui10 小时前
Android 系统服务的添加
android
蜡台11 小时前
Flutter Container 与装饰完整讲解
android·javascript·flutter·dart
杉氧12 小时前
用 Compose 挑战交互与动效天花板:ComposeCraftLab 开源实验室全解析
android·前端·kotlin
朝与同歌暮同酒12 小时前
冒泡社区《幻想三国》还能玩吗?安卓手机与电脑模拟器试玩记录
android·智能手机·电脑
音视频牛哥13 小时前
从数字孪生到机器人操控:Android Unity3D下RTMP/RTSP多路低延迟播放实践
android·unity·音视频·unity rtsp播放器·unity rtmp播放器·rtsp player·rtmp player