一.表设计和xml中要考虑的问题:
这是承接上两篇文章,sql使用规范和oracle的安装使用。
当我想要去使用oracle作为数据库存储数据,一定是要对数据库数据产生操作的。那么有三个地方需要考虑:
1.数据库表的设计,要考虑全面;例如字段有哪些,合适的类型和长度等,这个在下面展开
2.字段设计之后,在数据库中如何正确命名?映射到xml文件中又该怎么命名?
3.非空,索引,主键自增等问题是否考虑?(唯一约束本人很少用到,这里暂不分析)
二.分析user表作为参考案例:
1.user表是人的基本信息的抽象,至少有以下信息:
姓名 | 性别 | 年龄 | 身份证号 | 手机号 | 住址 |
可能在平时生活中要email的较少,但确实很常见,且一个手机号联系不保险的。
2.隐藏信息是要考虑的:
建表用户 | 建表时间 | 修改用户 | 修改时间 | 逻辑删除状态值 |
这里是为了表的安全性,可以查到时间和操作的用户的信息。且数据不再使用物理删除而是逻辑删除,所以状态值也是要考虑的。
3.唯一标识:主键ID
ID |
这里的ID作为列名是否可行? 我在项目看到是最好不要。要用"PK_表名" 请各位探讨一下!
4.字段的类型
从ID来考虑,是varchar还是integer比较好呢?
如果是仅仅oracle的话,因为序列是从1开始,所以integer就是OK的。由于我不仅用到了oracle,还有mongo,而mongo的id是自动生成的,且较长了。所以综合考虑,同一为varchar
从身份证来考虑,最好是使用varchar,为了保证要么一整个字符串都失效要么都完好,不能确实一部分。
从性别来考虑,我习惯0代表男,1代表女,所以采用integer类型。
而创建时间,这种时间类型,是用date类型,还是时间戳timestamp类型呢?
以上是我的部分考虑。也请各位按照这个思路去综合考虑。
数据库存储时间的存储类型大概有3种,varchar2,date 和 timestamp
存储到秒级别,用date,
需要到秒级以下,用timestamp,
TIMESTAMP[(seconds_precision)]存 储世纪、4位的年、月、日、时(以24小时格式)、分和秒。seconds_precision为可选参数,用于指定精度,该参数为一个整数,范围从 0~9,默认值为9;意思是表示秒的数字的小数点右边可以存储9位数字。如果试图在秒的小数中保存超过TIMESTAMP存储精度的数字,那么该小数将被 取整
且timestamp可以存储时区
oracle存储时间类型 date 和 timestamp区别_oracle date和timestamp区别-优快云博客
5.每个字段长度
字段长度是要考虑的,不能说初学建表就不去考虑。而是更应该考虑。这样逻辑上才不那么会出现漏洞,不然后期改起来是很痛苦的。
我考虑第一,长度为1的。比如sex,delStatus.
然后长度固定的,idCard,phone。
以及合理长度,比如姓名,百度表示最长名字是25个字,那么有没有必要设置长度为75个字节?(中文每一个汉字占三个字节)。
6.有没有哪些字段是非空的?
7.每个字段的注释有,且sex这种字段,0、1代表什么意思一定要写明白
8.比如sex,delStuatus这些字段是要设置默认值的
9.为了快速的查询,部分字段要加上索引。那么两个问题,1.索引加在哪个字段上,2.是唯一索引还是普通索引。
根据我的预设需求,会通过名字来查,所以会给名字加上普通索引。
10.主键ID是要做自增设计的。
所以在建表语句中创建了一个sequence(序列),但是从稳定性来看,最好是在xml中写sql的时候再用上,而不要使用触发器。这会产生一定的不稳定性。
在 Oracle 数据库中,可以通过创建一个
SEQUENCE
来实现自增主键。SEQUENCE
是一个对象,它生成唯一的数字序列,可以被用来给主键赋值。你需要在创建表之前创建一个SEQUENCE
,然后在插入新行数据时,通过NEXTVAL
函数调用SEQUENCE
从而获得下一个自增值
CREATE SEQUENCE table_name_seq START WITH 1 INCREMENT BY 1;
你需要将 table_name_seq 替换为你想要使用的名称,以及根据需要更改 START WITH 和 INCREMENT BY 值。 START WITH 指定初始序列值,INCREMENT BY 指定每次调用 NEXTVAL 函数时序列增加的步长
以下是部分sql示例:
新建一张表
create table test
(id int primary key,
name varchar2(10));
create sequence 序列名称
start with 1 -- 起始值
increment by 1 -- 增量
maxvalue 99999999 -- 最大值
nocycle -- 达到最大值后是否重新计算,当前为不重新计算,cycle为重新计算
nocache; -- 不要缓存,容易跳号
不用触发器:
insert into student values (zsc_seq.nextval,'zsc');
触发器实现(不要改动,就当做一个脚本)
CREATE OR REPLACE TRIGGER 触发器名称
BEFORE INSERT ON 表名称 FOR EACH ROW
BEGIN
SELECT 序列名称.NEXTVAL INTO :NEW.字段名称 FROM DUAL;
END;
删除序列:
在删除一个序列之前,您需要确认要删除的序列的名称。您可以使用以下命令查看所有可用的序列
SELECT sequence_name FROM user_sequences;
在删除一个序列之前,您需要停止使用它。如果有表在使用该序列,那么它不能被删除。使用以下命令检查表是否正在使用该序列:
SELECT table_name FROM user_tab_columns WHERE column_name = 'PK_USERS_ID';
如果有表正在使用该序列,您将需要先修改表。您可以使用以下命令修改表,使其不再使用该序列:
ALTER TABLE BOYANZSC_TORACLEDEMO_USERS MODIFY (PK_USERS_ID DEFAULT NULL);
一旦您的序列不再被使用,您可以使用以下命令来删除它:
DROP SEQUENCE PK_USERS_ID;
参考文章:
创建序列和触发器https://www.cnblogs.com/chenzblog/p/15657006.html
删除序列https://www.php.cn/faq/533076.html
三.映射到xml
<mapper namespace="com.zsc.dao.UserMapper">
<resultMap id="BaseResultMap" type="com.zsc.po.ToracleDemoUserPO">
<id column="PK_USER" jdbcType="INTEGER" property="userId" />
<result column="USER_NAME" jdbcType="VARCHAR" property="userName"/>
<result column="AGE" jdbcType="INTEGER" property="age"/>
<result column="SEX" jdbcType="INTEGER" property="sex"/>
<result column="IDCARD" jdbcType="VARCHAR" property="idCard"/>
<result column="PHONENUM" jdbcType="INTEGER" property="phoneNum"/>
<result column="EMAIL" jdbcType="VARCHAR" property="email"/>
<result column="CREATE_USER" jdbcType="VARCHAR" property="createUser"/>
<result column="CREATE_TIME" jdbcType="TIMESTAMP" property="createTime"/>
<result column="UPDATE_USER" jdbcType="VARCHAR" property="updateUser"/>
<result column="UPDATE_TIME" jdbcType="TIMESTAMP" property="updateTime"/>
<result column="DEL_STATUS" jdbcType="INTEGER" property="delStatus"/>
<result column="ADDRESS_ID" jdbcType="VARCHAR" property="userAddressId"/>
</resultMap>
<sql id="Base_Column_List">
PK_USER,
USER_NAME,
AGE,
SEX,
IDCARD,
PHONENUM,
EMAIL,
CREATE_USER,
CREATE_TIME,
UPDATE_USER,
UPDATE_TIME,
DEL_STATUS,
ADDRESS_ID
</sql>
这两块是一定要有的,后面的用处很大。比如BaseResultMap可以作为返回的类型,Base_Column_List可以替代全表字段,避免*的使用。
四.sequence的使用
刚才提到了sequence不要在数据库中去使用,那么在sql中如何使用?
一般是结合新增这一类的sql使用
<insert id="saveUser" parameterType="com.zsc.po.ToracleDemoUserPO">
<selectKey resultType="java.lang.Integer" keyProperty="shiftid" order="BEFORE">
SELECT SEQ_USERID.nextval FROM dual
</selectKey>
INSERT INTO BOYANZSC_TORACLEDEMO_USER
<trim prefix="(" suffix=")" suffixOverrides="," >
<if test="shiftid != null">
USER_ID,
</if>
<if test="savePO.userName != null">
USER_NAME,
</if>
<if test="savePO.age != null">
AGE,
</if>
<if test="savePO.sex != null">
SEX,
</if>
<if test="savePO.idCard != null">
IDCARD,
</if>
<if test="savePO.phoneNum != null">
PHONENUM,
</if>
<if test="savePO.email != null">
EMAIL,
</if>
<if test="savePO.createUser != null">
CREATE_USER,
</if>
<if test="savePO.createTime != null">
CREATE_TIME,
</if>
<if test="savePO.updateUser != null">
UPDATE_USER,
</if>
<if test="savePO.updateTime != null">
UPDATE_TIME,
</if>
<if test="savePO.delStatus != null">
DEL_STATUS,
</if>
<if test="savePO.userAddressId != null">
ADDRESS_ID
</if>
</trim>
VALUES
<trim prefix="(" suffix=")" suffixOverrides="," >
<if test="shiftid != null">
#{shiftid},
</if>
<if test="savePO.userName != null">
#{savePO.userName},
</if>
<if test="savePO.age != null">
#{savePO.age},
</if>
<if test="savePO.sex != null">
#{savePO.sex},
</if>
<if test="savePO.idCard != null">
#{savePO.idCard},
</if>
<if test="savePO.phoneNum != null">
#{savePO.phoneNum},
</if>
<if test="savePO.email != null">
#{savePO.email},
</if>
<if test="savePO.createUser != null">
#{savePO.createUser},
</if>
<if test="savePO.createTime != null">
#{savePO.createTime},
</if>
<if test="savePO.updateUser != null">
#{savePO.updateUser},
</if>
<if test="savePO.updateTime != null">
#{savePO.updateTime},
</if>
<if test="savePO.delStatus != null">
#{savePO.delStatus},
</if>
<if test="savePO.userAddressId != null">
#{savePO.userAddressId},
</if>
</trim>
</insert>
这里将序列值从dual虚拟表中查出来,代替ID这一类的数据值。
五.分页的sql,用到oracle的另一个知识叫做"行号" ROWNUM
ROWNUM 也是一个保留字所以无法直接在查处行号的那一层直接用行号比较,所以要取别名,降级之后,在外层才可以使用行号去作比较。
<select id="queryUserList" resultMap="BaseResultMap">
select
toUserPage.USER_ROWNUM,<include refid="Base_Column_List"/>
from
(select
ROWNUM USER_ROWNUM, <include refid="Base_Column_List" />
from
BOYANZSC_TORACLEDEMO_USER
<where>
<if test="PagePO.userName != null and PagePO.userName != ''">
USER_NAME like '%' || #{PagePO.userName} || '%'
</if>
<if test="PagePO.sex != null and PagePO.sex != ''">
and SEX = #{PagePO.sex}
</if>
<if test="PagePO.age != null and PagePO.age != ''">
and AGE = #{PagePO.age}
</if>
and DEL_STATUS = 0
</where>) toUserPage
where
USER_ROWNUM <![CDATA[>]]> #{pagePaeams.start}
AND
USER_ROWNUM <![CDATA[<=]]> #{pagePaeams.finish}
</select>
六.批量插入,对于子查询不让用select * ,才如何解决
方法就是按照传入的参数里面的字段,去分别取别名。而且select后面直接全部陈列出来。(这是现在的办法,后续应该会优化)
<insert id="batchInsertUser" parameterType="java.util.List" useGeneratedKeys="false">
INSERT INTO BOYANZSC_TORACLEDEMO_USER (
PK_USER,
USER_NAME,
AGE,
SEX,
IDCARD,
PHONENUM,
EMAIL,
CREATE_USER,
CREATE_TIME,
UPDATE_USER,
UPDATE_TIME,
DEL_STATUS,
ADDRESS_ID
)
SELECT
SEQ_USERID.nextval,
batch.USERNAME,
batch.AGE,
batch.SEX,
batch.IDCARD,
batch.PHONENUM,
batch.EMAIL,
batch.CREATEUSER,
batch.CREATETIME,
batch.UPDETEUSER,
batch.UPDATETIME,
batch.DELSTATUS,
batch.ADDRESSID
FROM(
<foreach collection="toracleDemoUserPOList" item="item" separator="union all">
SELECT
#{item.userName} as USERNAME,
#{item.age} as AGE,
#{item.sex} as SEX,
#{item.idCard} as IDCARD,
#{item.phoneNum} as PHONENUM,
#{item.email} as EMAIL,
#{item.createUser} as CREATEUSER,
#{item.createTime} as CREATETIME,
#{item.updateUser} as UPDETEUSER,
#{item.updateTime} as UPDATETIME,
#{item.delStatus} as DELSTATUS,
#{item.userAddressId} as ADDRESSID
FROM
dual
</foreach>
)batch
</insert>