后台首页+EasyUI+分类树+商品列表
思考:
序号 | 知识点 | 类型 | 难度系数 | 掌握程度 |
| EasyUI富客户端 | 技术 | 3 | 了解 |
| 重复扫描问题: Spring注解方式读取属性文件中的属性为何不能在只能在service层注入,不能在controller层注入? | 论述 | 1 | 了解 |
| MyBatis假接口 | 论述 | 1 | 理解 |
知识点:
序号 | 知识点 | 类型 | 难度系数 | 掌握程度 |
| EasyUI.tree组件,异步加载树 | 设计 | 3 | 熟练 |
| EasyUI.datagrid组件 | 设计 | 1 | 熟练 |
数据库表设计
先创建下面三张表,后续根据业务再创建其所需要的数据库表。
商品分类表
create table tb_item_cat
(
id bigint not null auto_increment,
parent_id bigint comment '父分类ID=0时,代表一级分类',
name varchar(150),
status int(1) default 1 comment '默认值为1,可选值:1正常,2删除',
sort_order int(4) not null,
is_parent tinyint(1),
created datetime,
updated datetime,
primary key (id)
);
create index parent_id on tb_item_cat
(
parent_id,
status
);
create index sort_order on tb_item_cat
(
sort_order
);
典型树形结构,创建索引。
注意表设计的不同:
- 表的主键采用长整形
- 对parent_id和sort_order常用查询条件字段设置索引
商品分类的显示
要求:点击“选择分类”,弹出窗口esayUI.window,esayUI.tree树形展现分类表tb_item_cat中的数据。只能选最后的分类(叶子节点)。
easyUI 官网: http://www.jeasyui.net/
通用Mapper的插件介绍
注:Selective代表不是所有字段拼入SQL。例如:insert是所有字段都拼入SQL语句,而insertSelective代表非空字段才拼入SQL语句。这样insertSelective比insert语句执行效率高。
问题:
传统的mybatis需要在映射文件中写sql,由于业务的不同造成sql语句不同,而无法造成通用,开发人员工作量巨大,也不易维护。
解决:
- 把表名作为参数传入,不能解决字段不同的问题。
- 动态生成SQL,如何实现?可以使用插件(拦截器实现)。
- 这样映射文件中只需写表和实体的映射关系
依赖
官网地址:http://git.oschina.net/free/Mapper
提示,3.1.0及以后版本的groupId修改为tk.mybatis, artifactId为mapper
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper</artifactId>
<version>3.2.2</version>
</dependency>
JPA注解
JPA全称Java Persistence API.JPA通过JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。Sun引入新的JPA ORM规范出于两个原因:其一,简化现有Java EE和Java SE应用开发工作;其二,Sun希望整合ORM技术,实现天下归一。 JPA由EJB 3.0软件专家组开发。
在POJO实体对象上加JPA(Java Persistence API)注解,实现类和数据库表之间的映射。
序号 | javax.persistence注解 | 说明 |
| @Table(name="tb_item_cat") | 标识类对应的表 |
| @Id | 主键 |
| @GeneratedValue (strategy=GenerationType.IDENTITY) | 自增 |
| @Column(name="parent_id") | 当属性和字段不一致,设置映射关系 |
未指定的驼峰规则,在mybatis-config.xml中配置
<setting name="mapUnderscoreToCamelCase" value="true"/>。
1)表名默认使用类名,按驼峰规则,下划线后字母自动大写,如UserInfo对应表名user_info。
2)当表名和类名不对应时,使用@Table(name="tableName")进行指定。
3)属性和字段名不对应时,使用@Column(name="fieldName")进行指定。
4)当非表的属性时,使用@Transient可以忽略此属性。
5)必须设置主键@Id,可以有多个属性设置@Id,作为联合主键。默认情况下,如果没有设置主键,所有字段作为一个联合主键,这种效率极低。
6)实体类可以继承使用,注解也将继承。
7)基于基本类型,如int作为实体字段时会有默认值0,而且无法消除,所以实体类中建议不要使用基本类型。
8)主键策略,支持序列、UUID、主键自增三种方式,其中序列和UUID可以配置多个,主键自增只能配置一个。
@GeneratedValue(strategy = GenerationType.IDENTITY) //主键自增长策略
@GeneratedValue(strategy=GenerationType.AUTO) 也是默认策略, 即写成@GeneratedValue也可。类似于hibernate的native策略,生成方式取决于底层的数据库。
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_u")指定“序列”策略,使用于Oracle。其中generator表示序列的名字。
@GeneratedValue(generator = "UUID")可以用于任意字符串类型长度超过32位的字段。
通用Mapper Demo示例
第一步:创建ManvenWeb工程demo-supermapper
依赖通用mapper jar包,采用2.3.2版本。加入其它的jar包依赖。
pom.xml文件代码如下:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.supermapper</groupId>
<artifactId>demo-supermapper</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>demo-supermapper Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>com.github.abel533</groupId>
<artifactId>mapper</artifactId>
<version>2.3.2</version>
</dependency>
<!-- 单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
<!-- Mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.8</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.2.2</version>
</dependency>
<!-- MySql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.32</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.4</version>
</dependency>
</dependencies>
<build>
<finalName>demo-supermapper</finalName>
</build>
</project>
第二步:配置mybatis核心配置文件mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 引入外部的配置文件 -->
<properties resource="jdbc.properties"/>
<settings>
<!-- 开启驼峰自动映射 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<typeAliases>
<!-- 设置别名 -->
<package name="com.supermapper.pojo"/>
</typeAliases>
<!-- 指定环境 -->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</dataSource>
</environment>
</environments>
<mappers>
<package name="com.supermapper.mapper"/>
</mappers>
</configuration>
第三步:数据库链接参数配置jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/jtdb?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
jdbc.username=root
jdbc.password=root
第四步:日志配置log4j.properties
log4j.rootLogger=DEBUG,A1
log4j.logger.org.mybatis = DEBUG
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c]-[%p] %m%n
第五步:创建测试表tb_user
create table t_user
(
id bigint not null auto_increment,
user_name varchar(30),
password varchar(30),
name varchar(50),
age tinyint,
sex tinyint,
birthday datetime,
created datetime,
updated datetime,
primary key (id)
);
第六步:创建实体对象User.java
POJO实现代码如下:
package com.jt.test.pojo;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Table(name = "t_user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) //主键自增长策略
private Long id;
@Column(name = "user_name")
private String userName;
private String password;
//@Column
private String name;
private Integer age;
private Boolean sex;
private Date birthday;
private Date created;
//@Transient 不是数据库表字段,忽略
private Date updated;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Boolean getSex() {
return sex;
}
public void setSex(Boolean sex) {
this.sex = sex;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public Date getCreated() {
return created;
}
public void setCreated(Date created) {
this.created = created;
}
public Date getUpdated() {
return updated;
}
public void setUpdated(Date updated) {
this.updated = updated;
}
@Override
public String toString() {
return "User [id=" + id + ", userName=" + userName + ", password=" + password + ", name=" + name
+ ", age=" + age + ", sex=" + sex + ", birthday=" + birthday + ", created=" + created
+ ", updated=" + updated + "]";
}
}
第七步:引入对通用接口封装类和接口SysMapper
SysMapperPrivider.java
package com.supermapper.base.mapper;
import static org.apache.ibatis.jdbc.SqlBuilder.BEGIN;
import static org.apache.ibatis.jdbc.SqlBuilder.DELETE_FROM;
import static org.apache.ibatis.jdbc.SqlBuilder.SQL;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.scripting.xmltags.ForEachSqlNode;
import org.apache.ibatis.scripting.xmltags.MixedSqlNode;
import org.apache.ibatis.scripting.xmltags.SqlNode;
import org.apache.ibatis.scripting.xmltags.StaticTextSqlNode;
import com.github.abel533.mapper.MapperProvider;
import com.github.abel533.mapperhelper.EntityHelper;
import com.github.abel533.mapperhelper.MapperHelper;
public class SysMapperProvider extends MapperProvider {
public SysMapperProvider(Class<?> mapperClass, MapperHelper mapperHelper) {
super(mapperClass, mapperHelper);
}
public SqlNode deleteByIDS(MappedStatement ms) {
Class<?> entityClass = getSelectReturnType(ms);
Set<EntityHelper.EntityColumn> entityColumns = EntityHelper.getPKColumns(entityClass);
EntityHelper.EntityColumn column = null;
for (EntityHelper.EntityColumn entityColumn : entityColumns) {
column = entityColumn;
break;
}
List<SqlNode> sqlNodes = new ArrayList<SqlNode>();
// 开始拼sql
BEGIN();
// delete from table
DELETE_FROM(tableName(entityClass));
// 得到sql
String sql = SQL();
// 静态SQL部分
sqlNodes.add(new StaticTextSqlNode(sql + " WHERE " + column.getColumn() + " IN "));
// 构造foreach sql
SqlNode foreach = new ForEachSqlNode(ms.getConfiguration(), new StaticTextSqlNode("#{"
+ column.getProperty() + "}"), "ids", "index", column.getProperty(), "(", ")", ",");
sqlNodes.add(foreach);
return new MixedSqlNode(sqlNodes);
}
}
SySMapper.java
package com.supermapper.base.mapper;
import java.util.List;
import org.apache.ibatis.annotations.DeleteProvider;
import org.apache.ibatis.annotations.InsertProvider;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.SelectProvider;
import org.apache.ibatis.annotations.UpdateProvider;
public interface SysMapper<T> {
/**
* 根据主键ID批量删除
*
* @param key
* @return
*/
@DeleteProvider(type = SysMapperProvider.class, method = "dynamicSQL")
int deleteByIDS(@Param("ids") Object[] key);
/**
* 根据实体类不为null的字段进行查询,条件全部使用=号and条件
*
* @param record
* @return
*/
@SelectProvider(type = SysMapperProvider.class, method = "dynamicSQL")
List<T> select(T record);
/**
* 根据实体类不为null的字段查询总数,条件全部使用=号and条件
*
* @param record
* @return
*/
@SelectProvider(type = SysMapperProvider.class, method = "dynamicSQL")
int selectCount(T record);
/**
* 根据主键进行查询,必须保证结果唯一 单个字段做主键时,可以直接写主键的值 联合主键时,key可以是实体类,也可以是Map
*
* @param key
* @return
*/
@SelectProvider(type = SysMapperProvider.class, method = "dynamicSQL")
T selectByPrimaryKey(Object key);
/**
* 插入一条数据 支持Oracle序列,UUID,类似Mysql的INDENTITY自动增长(自动回写) 优先使用传入的参数值,参数值空时,才会使用序列、UUID,自动增长
*
* @param record
* @return
*/
@InsertProvider(type = SysMapperProvider.class, method = "dynamicSQL")
int insert(T record);
/**
* 插入一条数据,只插入不为null的字段,不会影响有默认值的字段 支持Oracle序列,UUID,类似Mysql的INDENTITY自动增长(自动回写)
* 优先使用传入的参数值,参数值空时,才会使用序列、UUID,自动增长
*
* @param record
* @return
*/
@InsertProvider(type = SysMapperProvider.class, method = "dynamicSQL")
int insertSelective(T record);
/**
* 根据实体类中字段不为null的条件进行删除,条件全部使用=号and条件
*
* @param key
* @return
*/
@DeleteProvider(type = SysMapperProvider.class, method = "dynamicSQL")
int delete(T key);
/**
* 通过主键进行删除,这里最多只会删除一条数据 单个字段做主键时,可以直接写主键的值 联合主键时,key可以是实体类,也可以是Map
*
* @param key
* @return
*/
@DeleteProvider(type = SysMapperProvider.class, method = "dynamicSQL")
int deleteByPrimaryKey(Object key);
/**
* 根据主键进行更新,这里最多只会更新一条数据 参数为实体类
*
* @param record
* @return
*/
@UpdateProvider(type = SysMapperProvider.class, method = "dynamicSQL")
int updateByPrimaryKey(T record);
/**
* 根据主键进行更新 只会更新不是null的数据
*
* @param record
* @return
*/
@UpdateProvider(type = SysMapperProvider.class, method = "dynamicSQL")
int updateByPrimaryKeySelective(T record);
}
第八步:创建接口UserMapper.xml
继承Mapper接口,在这个通用的接口中定义了常用的方法,如有特殊,在这个接口中配置。
package com.supermapper.mapper;
import com.supermapper.base.mapper.SysMapper;
import com.supermapper.pojo.User;
/*
* 注意必须继承SysMapper,不能继承Mapper,否则报错:
* org.apache.ibatis.binding.BindingException: Type interface com.supermapper.mapper.UserMapper is not known to the MapperRegistry.
*/
public interface UserMapper extends SysMapper<User>{
}
第九步:测试类,查询所有记录
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import com.github.abel533.mapperhelper.MapperHelper;
import com.supermapper.base.mapper.SysMapper;
import com.supermapper.mapper.UserMapper;
import com.supermapper.pojo.User;
public class TestSuperMapper {
public static void main(String[] args) throws IOException {
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sessionFactory.openSession(true);
// 创建一个MapperHelper
MapperHelper mapperHelper = new MapperHelper();
// 主键自增回写方法,默认值MYSQL,详细说明请看文档
mapperHelper.setIDENTITY("MYSQL");
// 注册通用Mapper接口
mapperHelper.registerMapper(SysMapper.class);
// 配置完成后,执行下面的操作
mapperHelper.processConfiguration(session.getConfiguration());
// OK - mapperHelper的任务已经完成,可以不管了
UserMapper mapper = session.getMapper(UserMapper.class);
List<User> uList = mapper.select(null);
for(User u : uList){
System.out.println(u);
}
}
}
控制台日志
2015-07-02 15:00:12,825 [main] [com.supermapper.mapper.UserMapper.select]-[DEBUG] ==> Preparing: SELECT UPDATED,BIRTHDAY,NAME,USER_NAME USERNAME,CREATED,SEX,PASSWORD,ID,AGE FROM t_user
2015-07-02 15:00:12,924 [main] [com.supermapper.mapper.UserMapper.select]-[DEBUG] ==> Parameters:
2015-07-02 15:00:13,010 [main] [com.supermapper.mapper.UserMapper.select]-[DEBUG] <== Total: 1
User [id=1, userName=王致和, password=123456, name=纸盒, age=30, sex=false, birthday=Tue Jun 09 00:00:00 CST 2015, created=Wed Jun 10 00:00:00 CST 2015, updated=Thu Jun 11 00:00:00 CST 2015]
从日志中可以看出执行的SQL语句。我们并没有在mybatis映射文件中配置字段,它怎么知道的呢?它是通过分析pojo对象,并通过jpa注解获取了字段的名称。这样开发者的工作量大量减少,并方便维护。