mysql5.7版本之后支持json字段类型,推荐mysql8版本,适用于属性不确定的个性化字段,比如:
身份信息{“职业”,“学生”,“兴趣”:“打乒乓球”,“特长”:“跳高,书法”};
图片信息{“日期”:“2023-12-12 22:12”,“尺寸”:“215*720”,“拍摄地”:“xxx”,“作者”:“xxx”};
标签信息:[“历史”,“军事”,“都市”,“科幻”]。
这部分信息每条数据差异较大,固定字段会大量冗余,使用json有较高的扩展性和自由度
JSON对象除了支持字符串、整型、日期类型,JSON 内嵌的字段也支持数组类型
CREATE TABLE UserLogin (
userId BIGINT NOT NULL,
loginInfo JSON,
PRIMARY KEY(userId)
);
SET @a = '
{
"cellphone" : "1",
"wxchat" : "码农",
"77" : "1"
}';
INSERT INTO UserLogin VALUES (1,@a);
SELECT
userId,
JSON_UNQUOTE(JSON_EXTRACT(loginInfo,"$.cellphone")) cellphone,
JSON_UNQUOTE(JSON_EXTRACT(loginInfo,"$.wxchat")) wxchat
FROM UserLogin;
等效于
SELECT
userId,
loginInfo->>"$.cellphone" cellphone,
loginInfo->>"$.wxchat" wxchat
FROM UserLogin;
CREATE TABLE `photos ` (
`id` bigint NOT NULL COMMENT '文件在云端的唯一标识ID',
`tags` json DEFAULT NULL COMMENT '标签数组',
`info` json DEFAULT NULL COMMENT '照片生成信息json',
PRIMARY KEY (`id`) USING BTREE
);
-- json字段属性筛选,查询拍摄地是重庆的
SELECT * FROM photos WHERE JSON_EXTRACT(info, '$.拍摄地') = '重庆';
-- 查询标签包含10的图片
SELECT * FROM photos WHERE 10 MEMBER OF(tags->"$");
-- 查询标签包含2和10的图片
SELECT * FROM photos WHERE JSON_CONTAINS(tags->"$", '[2,10]');
-- 或者
SELECT * FROM photos WHERE JSON_OVERLAPS(tags->"$", '[2,10]');
JSON_CONTAINS 返回json数组是否包含指定的数组
JSON_OVERLAPS 返回json数组是否与指定的数组有交集
相当于增加虚拟列
-- 为json中的手机号建立索引
ALTER TABLE UserLogin ADD COLUMN cellphone VARCHAR(255) AS (loginInfo->>"$.cellphone");
ALTER TABLE UserLogin ADD UNIQUE INDEX idx_cellphone(cellphone);
-- 也可以在建表时创建索引
CREATE TABLE UserLogin (
userId BIGINT,
loginInfo JSON,
cellphone VARCHAR(255) AS (loginInfo->>"$.cellphone"),
PRIMARY KEY(userId),
UNIQUE KEY uk_idx_cellphone(cellphone)
);
-- 使用索引的虚拟列查询
SELECT * FROM UserLogin WHERE cellphone = '11'
mysql与java实体常见的字段映射不需要我们特别指定,json字段因为其灵活性,需要单独指定。
我这里列举出上例中photos表的json与java实体的map和list的转换,
如果你的java实体中json字段映射的其他类型直接在示例转换上修改类型即可。
(也可以用泛型写通用转换)
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Map;
public class CustomJsonTypeHandler implements TypeHandler<Map<String, Object>> {
private static final ObjectMapper objectMapper = new ObjectMapper();
@Override
public void setParameter(PreparedStatement ps, int i, Map<String, Object> parameter, JdbcType jdbcType) throws SQLException {
try {
String json = objectMapper.writeValueAsString(parameter);
ps.setString(i, json);
} catch (JsonProcessingException e) {
throw new SQLException("Error converting Map to JSON");
}
}
@Override
public Map<String, Object> getResult(ResultSet rs, String columnName) throws SQLException {
String json = rs.getString(columnName);
return fromJson(json);
}
@Override
public Map<String, Object> getResult(ResultSet rs, int columnIndex) throws SQLException {
String json = rs.getString(columnIndex);
return fromJson(json);
}
@Override
public Map<String, Object> getResult(CallableStatement cs, int columnIndex) throws SQLException {
String json = cs.getString(columnIndex);
return fromJson(json);
}
private Map<String, Object> fromJson(String json) throws SQLException {
if (json == null || json.isEmpty()) {
return null;
}
try {
return objectMapper.readValue(json, Map.class);
} catch (JsonProcessingException e) {
throw new SQLException("Error converting JSON to Map");
}
}
}
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
public class JsonArrayTypeHandler implements TypeHandler<List<String>> {
private static final ObjectMapper objectMapper = new ObjectMapper();
@Override
public void setParameter(PreparedStatement ps, int i, List<String> parameter, JdbcType jdbcType) throws SQLException {
try {
String json = objectMapper.writeValueAsString(parameter);
ps.setString(i, json);
} catch (JsonProcessingException e) {
throw new SQLException("Error converting Integer[] to JSON");
}
}
@Override
public List<String> getResult(ResultSet rs, String columnName) throws SQLException {
String json = rs.getString(columnName);
return fromJson(json);
}
@Override
public List<String> getResult(ResultSet rs, int columnIndex) throws SQLException {
String json = rs.getString(columnIndex);
return fromJson(json);
}
@Override
public List<String> getResult(CallableStatement cs, int columnIndex) throws SQLException {
String json = cs.getString(columnIndex);
return fromJson(json);
}
private List<String> fromJson(String json) throws SQLException {
if (json == null || json.isEmpty()) {
return null;
}
try {
return objectMapper.readValue(json, List.class);
} catch (JsonProcessingException e) {
throw new SQLException("Error converting JSON to Integer[]");
}
}
}
-- 新增信息
<insert id="insertPhotos" parameterType="Photos">
insert into photos
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="tags != null">tags,</if>
<if test="info != null">info,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="tags != null">#{tags, typeHandler=com.ruoyi.web.core.config.JsonArrayTypeHandler},
<if test="info != null">#{info,typeHandler=com.ruoyi.web.core.config.CustomJsonTypeHandler},
</trim>
</insert>
-- 修改信息
<update id="updatePhotos" parameterType="Photos">
update photos
<trim prefix="SET" suffixOverrides=",">
<if test="info != null">info = #{info,typeHandler=com.ruoyi.web.core.config.CustomJsonTypeHandler},
<if test="tags != null">tags = #{tags,typeHandler=com.ruoyi.web.core.config.JsonArrayTypeHandler},
</trim>
where id= #{id}
</update>
<resultMap type="Photos" id="PhotosResult">
<result property="id" column="id"/>
<result property="tags" column="tags" javaType="java.util.List" typeHandler="com.ruoyi.web.core.config.JsonArrayTypeHandler"/>
<result property="info" column="tags" javaType="java.util.List" typeHandler="com.ruoyi.web.core.config.CustomJsonTypeHandler"/>
</resultMap>
<select id="PhotosList" parameterType="Photos" resultMap="PhotosResult">
<include refid="selectPhotosVo"/>
<where>
<if test="id != null ">and id = #{id}
<if test="tags != null ">
and JSON_CONTAINS(tags->"$",
#{tags, typeHandler=com.ruoyi.web.core.config.JsonArrayTypeHandler}
)
</if>
</where>
</select>
部分json查询语法参考文章:https://mp.weixin.qq.com/s?src=11×tamp=1706855643&ver=5055&signature=xH0-aTP0U1liqYAzXaSvvqAqto5UUiPrJY5P1-qizaGNTpmSTV7ZY7qkqyMujiUWM8dhenWhjDLecMoMgeXBwMYffwSSbLKe2UrQ308ZaDBUaVJ4ku29U0X05XZh&new=1