项目中用到了从sql.xml中获取sql语句模板并且根据前端参数配合yml配置参数将处理后的参数封装到模板形成完整sql后发送到数据库查询的用法,写这篇博客想与大家分享一下JAXB的用法和从yml读取配置的用法。
一.读取yml配置文件中的配置属性:
yml文件有如下配置:
myConfig:
name: xiaoming
age: 18
sex: 1
socre: 100
获取yml自定义配置的类:
@Component
@ConfigurationProperties("myConfig")
public class YmlUtils {
private String name;
private String age;
private String sex;
private Integer score;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Integer getScore() {
return score;
}
public void setScore(Integer score) {
this.score = score;
}
}
Controller:
地址栏访问该TestController的test2地址:
二.yml配置封装到对象中:
yml配置修改如下:想要把people配置项封装到People对象中
myConfig:
people:
name: xiaoming
age: 18
sex: 1
score: 100
People类:
public class People implements Serializable {
private String name;
private String age;
private String sex;
get/set...
@Override
public String toString() {
return "People{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
", sex='" + sex + '\'' +
'}';
}
}
Controller:
访问效果:
当然也可以把yml文件中的配置封装到Map中(yml是ky格式的):
yml:
配置类:
项目启动后控制台:
小结:
①想读取yml配置的类必须被spring扫描到,即要么新添加一个类加上@Component,要么在启动类新加一个方法加上@Bean(启动类操作的方式与新添加一个类的方式不太一样,启动类操作的是ConnectionSettings,新添加的类操作的是自己定义的类,有兴趣的可以百度)
②不论是自定义类还是启动类操作,必须有@ConfigurationProperties(“父级配置名字”)这个注解
③自定义类的属性名一定要和yml文件的子级配置名保持一致
④读取yml内容会自动类型转换,如score为Integer,age为String
⑤获取yml也可以使用@Value("${父级name.子级name}")获取(@Value还有别的用法,有兴趣自行百度)
⑥yml的配置结构是父级配置(顶格)+子级配置属性(换行、tab键)(或者在父级配置的:后面回车即可自动格式化)
⑦set方法一定要有
三.从sql.xml中获取sql语句
有如下两个xml文件:
one.xml:
two.xml:
现在的想法是把所有的sql语句存入一个map中,k就是每个sql的id,v就是sql语句,这样在实际开发中在对应的方法中可以根据k来获取能满足功能的sql,十分方便。(two.xml的sql最后会解释,以及k怎么获取):
获取sql的工具类:
@XmlRootElement(name = "sql")
public class XmlUtils {
private Map<String, String> sqlMap = new HashMap<>();
private List<SelectSql> selects = new ArrayList<>();
@XmlElement(name = "select")
public List<SelectSql> getSelects() {
return selects;
}
public void setSelects(List<SelectSql> selects) {
this.selects = selects;
}
public Map<String, String> getSqlMap() {
return sqlMap;
}
public void setSqlMap(Map<String, String> sqlMap) {
this.sqlMap = sqlMap;
}
static class SelectSql {
private String id;
private String sql;
@XmlAttribute(name = "id")
public String getId() {
return id;
}
@XmlValue
@XmlJavaTypeAdapter(MyXmlAdapter.class)
public String getSql() {
return sql;
}
public void setId(String id) {
this.id = id;
}
public void setSql(String sql) {
this.sql = sql;
}
}
注解简单解释(网上拷贝):
①@XmlRootElement(name = “XXX”):类级别的注解,将类映射为xml全局元素,也就是根元素。简洁:从哪个根标签开始获取内容,例子为<sql>标签开始
②@XmlElement(name = “YYY”):字段,方法,参数级别的注解。该注解可以将被注解的字段(非静态),或者被注解的get/set方法对应的字段映射为本地元素,也就是子元素。默认使用字段名或get/set方法去掉前缀剩下部分小写作为元素名(在字段名和get/set方法符合命名规范的情况下)。简洁:要获取哪些子标签的内容,例子为<select>
③@XmlAttribute(name = “ZZZ”):字段和方法级别的注解。该注解会将字段或get/set方法对应的字段映射成本类对应元素的属性,属性名默认使用字段名或get/set方法去掉前缀剩下部分首字母小写(在字段名和get/set方法符合命名规范的情况下)。简洁:获取子标签的哪个字段内容,例子为id字段,因为要作为k
④@XmlValue:字段和方法级别的注解。该注解的作用,简单理解就是定义xml元素文本值的类型。简洁:具体作用萌新博主还不知道,大概就是注入子标签内容
⑤@XmlJavaTypeAdapter:包、类、字段,方法、参数级别的注解。可以对xml的内容自定义处理。简洁:对子标签内容进行自定义处理,如日期等,例子为去掉转义<![CDATA[ ]]>
⑥ @XmlAccessorType的默认访问级别是 XmlAccessType.PUBLIC_MEMBER(有兴趣自行百度),因此,如果java对象中的private成员变量设置了public权限的 getter/setter方法,就不要在private变量上使用@XmlElement和@XmlAttribute注解,否则在由java对象生成 xml时会报同一个属性在java类里存在两次的错误。
自定义处理标签内容的类:
public class MyXmlAdapter extends XmlAdapter<String,String> {
@Override
public String unmarshal(String v) {
if (StringUtils.isEmpty(v)){
return "";
}
String startStr="<![CDATA[";
String endStr="]]>";
if (v.contains(startStr)&&v.contains(endStr)) {
return v.substring(v.indexOf(startStr)+1,v.indexOf(endStr));
}
return v;
}
@Override
public String marshal(String v) {
return "<![CDATA[" + v + "]]>";
}
}
注意:
①marshal是对象–>xml文件,unmarshal是xml–>对象
②XmlAdapter的两个泛型,第一个是unmarshal的类型,第二个是marshal的类型
接下来就差最后一步,将获取的sql放入到map中:
@Configuration
public class LoadSqlConfig {
@Bean
public XmlUtils loadSQL(){
XmlUtils xmlUtils=new XmlUtils();
Map<String,String> sqlMap=new HashMap<>(8);
try {
JAXBContext context=JAXBContext.newInstance(XmlUtils.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
String[] strs={"one.xml","two.xml"};
for (String s : strs) {
Resource resource=new ClassPathResource("sql/"+s);
XmlUtils xml = (XmlUtils)unmarshaller.unmarshal(resource.getInputStream());
xml.getSelects().stream().forEach(e-> {
System.out.println(e.getId());
System.out.println(e.getSql());
sqlMap.put(e.getId(),e.getSql());
});
xmlUtils.getSelects().addAll(xml.getSelects());
}
xmlUtils.setSqlMap(sqlMap);
}catch (Exception e){
e.printStackTrace();
}
return xmlUtils;
}
}
①既然sql要放入map中,在项目运行时就要获取,那么应该在在项目启动时就初始化好,可以自定义类配置类,也可以在启动类操作。
②获取xml–>Java对象的unmarshaller 对象
JAXBContext context=JAXBContext.newInstance(XmlUtils.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
③sql是resources目录下的sql包,存放各种sql.xml
④获取内容:
XmlUtils xml = (XmlUtils)unmarshaller.unmarshal(resource.getInputStream());
一定要强转类型为自定义的sql工具类
⑤遍历一次,只是获取一个sql.xml的内容,所以最外层的xmlUtils才是我们需要的类
启动项目查看效果:
四.sql.xml文件有<select>、<update>等多个标签:
one.xml添加<update>:
XmlUtils修改:
@XmlElements({@XmlElement(name = "select"),@XmlElement(name = "update")})
public List<SelectSql> getSelects() {
return selects;
}
@XmlElements是要获取哪些子标签的内容,当然可以定义多个子标签。启动项目查看是否有one.xml的update语句:
五.实际用法
①大致思路就是如上所述,先把所有的sql.xml文件封装到工具类中,sqlMap的k则在yml中定义(也可以在conf.properties中定义,操作方式不同,有兴趣的可以百度下),通过@Value获取k,然后在相应的方法中获取相应的sql。
②two.xml的sql应该是比较常见的,
第一个sql是用于列表查询,只展示基本的信息:学生id,名字,头像,平均分
第二个sql查询详细信息,展示名字,性别,年龄,班级,各科分数等
在后端代码获取到初始的sql之后,是需要把参数绑定到sql语句中,最终形成可执行的sql,这需要手动封装一个类,把${XXX}替换成相应的实际参数。提示:sql转换工具类的方法参数传两个,一个是初始sql,一个是参数map,k为${paramName}的paramName,v为实际参数。
六.声明:
代码从来都不止一种写法,以上只是个人的用法。可能有不足的地方,只是给大家一个参考思路。