xml
-
jdbc
-
ejb
-
制定了 jpa (吸收了hibernate的成果)
-
支持 jdbcTemplate
配置數據源
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
- 默認爲你配置數據源,如:h2,hqldb,derby
- jpa用的 hibernate 。cloud 2.2.6 用5.4.12 。1.5.4 用 5.0.12 。都是hibernate final版本
- cloud 2.0.0.RELEASE 用 hibernate 5.0.1 final ,不知爲何
配置自定義數據源
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>//jpa照常引用
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>//引入msql驅動
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>//開始jdbc
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
</dependency> //使用dbcp2 數據源
spring.datasource.url=jdbc:mysql://localhost:3306/spring_boot_chapter5
spring.datasource.username=root
spring.datasource.password=123456
#spring.datasource.driver-class-name=com.mysql.jdbc.Driver
#指定數據庫連接池的類型 (配置dbcp2數據源)
spring.datasource.type=org.apache.commons.dbcp2.BasicDataSource
#最大等待連接中的數量,設 0 爲沒有限制
spring.datasource.dbcp2.max-idle=10
#最大連活動接數
spring.datasource.dbcp2.max-total=50
#最大等待毫秒數, 單位爲 ms, 超過時間會出錯誤信息
spring.datasource.dbcp2.max-wait-millis=10000
#數據庫連接池初始化連接數
spring.datasource.dbcp2.initial-size=5
監測數據庫連接池類型
- 2.0.0.RELEASE 版本演示成功
@Component //implements ApplicationContextAware
public class DataSourceShow implements ApplicationContextAware {
ApplicationContext applicationContext = null;
//重寫setApplicationContext
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
DataSource dataSource = applicationContext.getBean(DataSource.class);
System.out.println("--------------------------------");
System.out.println(dataSource.getClass().getName());
System.out.println("--------------------------------");
JdbcTemplate jdbcTemplate = applicationContext.getBean(JdbcTemplate.class);
System.out.println(jdbcTemplate.getClass().getName());
}
}
--------------------------------
org.apache.commons.dbcp2.BasicDataSource
--------------------------------
org.springframework.jdbc.core.JdbcTemplate
新建表
CREATE TABLE `t_user` (
`id` int(12) NOT NULL AUTO_INCREMENT,
`user_name` varchar(60) NOT NULL,
`sex` int(3) NOT NULL DEFAULT '1',
`note` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
新建pojo
public class User {
private Long id = null;
private String userName = null;
private String note = null;
private SexEnum sex = null;
}
public enum SexEnum {
MALE(1, "男"),
FEMALE(2, "女");
private int id ;
private String name;
SexEnum(int id, String name) {
this.id = id;
this.name= name;
}
public static SexEnum getEnumById(int id) {
//遍歷所有的值
for (SexEnum sex : SexEnum.values()) {
//如果某個值的 id 和參數 一樣,就返回這個數據對象
if (sex.getId() == id) {
return sex;
}
}
return null;
}
}
jdbc的使用
public interface JdbcTmplUserService {
public User getUser(Long id);
public List<User> findUsers(String userName, String note);
public int insertUser(User user);
public int updateUser(User user);
public int deleteUser(Long id);
public User getUser2(Long id);
public User getUser3(Long id);
}
@Service
public class JdbcTmplUserServiceImpl implements JdbcTmplUserService {
@Autowired
private JdbcTemplate jdbcTemplate = null;
// 獲取映射關係
private RowMapper<User> getUserMapper() {
// 使用Lambda表達式創建用戶映射關係
RowMapper<User> userRowMapper = (ResultSet rs, int rownum) -> {
User user = new User();
user.setId(rs.getLong("id"));
user.setUserName(rs.getString("user_name"));
int sexId = rs.getInt("sex");
SexEnum sex = SexEnum.getEnumById(sexId);
user.setSex(sex);
user.setNote(rs.getString("note"));
return user;
};
return userRowMapper;
}
// 獲取對象
@Override
public User getUser(Long id) {
// 執行的SQL
String sql = " select id, user_name, sex, note from t_user where id = ?";
// 參數
Object[] params = new Object[] { id };
User user = jdbcTemplate.queryForObject(sql, params, getUserMapper());
return user;
}
// 查詢用戶列表
@Override
public List<User> findUsers(String userName, String note) {
// 執行的SQL
String sql = " select id, user_name, sex, note from t_user " + "where user_name like concat('%', ?, '%') "
+ "and note like concat('%', ?, '%')";
// 參數
Object[] params = new Object[] { userName, note };
// 使用匿名類實現
List<User> userList = jdbcTemplate.query(sql, params, getUserMapper());
return userList;
}
// 插入數據庫
@Override
public int insertUser(User user) {
String sql = " insert into t_user (user_name, sex, note) values( ? , ?, ?)";
return jdbcTemplate.update(sql, user.getUserName(), user.getSex().getId(), user.getNote());
}
// 更新數據庫
@Override
public int updateUser(User user) {
// 執行的SQL
String sql = " update t_user set user_name = ?, sex = ?, note = ? " + " where id = ?";
return jdbcTemplate.update(sql, user.getUserName(), user.getSex().getId(), user.getNote(), user.getId());
}
// 刪除數據
@Override
public int deleteUser(Long id) {
// 執行的SQL
String sql = " delete from t_user where id = ?";
return jdbcTemplate.update(sql, id);
}
public User getUser2(Long id) {
// 通過Lambda表達式使用StatementCallback
User result = this.jdbcTemplate.execute((Statement stmt) -> {
String sql1 = "select count(*) total from t_user where id= " + id;
ResultSet rs1 = stmt.executeQuery(sql1);
while (rs1.next()) {
int total = rs1.getInt("total");
System.out.println(total);
}
// 執行的SQL
String sql2 = " select id, user_name, sex, note from t_user"
+ " where id = " + id;
ResultSet rs2 = stmt.executeQuery(sql2);
User user = null;
while (rs2.next()) {
int rowNum = rs2.getRow();
user= getUserMapper().mapRow(rs2, rowNum);
}
return user;
});
return result;
}
public User getUser3(Long id) {
// 通過Lambda表達式使用ConnectionCallback接口
return this.jdbcTemplate.execute((Connection conn) -> {
String sql1 = " select count(*) as total from t_user"
+ " where id = ?";
PreparedStatement ps1 = conn.prepareStatement(sql1);
ps1.setLong(1, id);
ResultSet rs1 = ps1.executeQuery();
while (rs1.next()) {
System.out.println(rs1.getInt("total"));
}
String sql2 = " select id, user_name, sex, note from t_user "
+ "where id = ?";
PreparedStatement ps2 = conn.prepareStatement(sql2);
ps2.setLong(1, id);
ResultSet rs2 = ps2.executeQuery();
User user = null;
while (rs2.next()) {
int rowNum = rs2.getRow();
user= getUserMapper().mapRow(rs2, rowNum);
}
return user;
});
}
}
- pojo plain ordinary java object
plain
adj. 平的;簡單的;樸素的;清晰的
n. 平原;無格式;樸實無華的東西
adv. 清楚地;平易地
ordinary
adj. 普通的;平凡的;平常的
n. 普通;平常的人(或事)
使用jpa 操作數據庫
-
java persistence api
-
java 持久化 api
persistence n. 持續;固執;存留;堅持不懈;毅力
-
定義了 對象 關係映射 orm 以及實體對象 持久化的標準接口
-
JPA 是 JSR-220 (EJB 3.0) 規範的一部分
jpa所維護的 核心是 實體,通過一個持久化 上下文來使用。上下文包含3部分
- 對象關係映射 Object Relational Mapping ORM 。支持註解 和 xml
- 實體操作 API ,實現對 實體對象的 CRUD操作
- 查詢語言,面向對象的查詢語言 JPQL java persistence Query Language 實現比較靈活的查詢
開發jpa
// 標明是一個實體類
@Entity(name = "user")
// 定義映射的表
@Table(name = "t_user")
@Alias(value = "user")// MyBatis指定別名
@Data
public class User {
// 標明主鍵
@Id
// 主鍵策略,遞增
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id = null;
// 定義屬性和表的映射關係
@Column(name = "user_name")
private String userName = null;
private String note = null;
// 定義轉換器
@Convert(converter = SexConverter.class)
private SexEnum sex = null;
}
性別轉換器
public class SexConverter
implements AttributeConverter<SexEnum, Integer>{
// 將枚舉轉換爲數據庫列
@Override
public Integer convertToDatabaseColumn(SexEnum sex) {
return sex.getId();
}
// 將數據庫列轉換爲枚舉
@Override
public SexEnum convertToEntityAttribute(Integer id) {
return SexEnum.getEnumById(id);
}
}
spring jpa 接口設計
Repository 下有接口 CrudRepository 下有接口 PagingAndSortingRepository 下有 JpaRepository
JpaRepository還實現了 QueryByExampleExecutor接口,擁有按照例子查詢的功能
測試代碼 action
@Controller
@RequestMapping("/jpa")
public class JpaController {
// 注入JPA接口,這裏不需要使用實現類
@Autowired
private JpaUserRepository jpaUserRepository = null;
@RequestMapping("/getUser")
@ResponseBody
public User getUser(Long id) {
// 使用JPA接口查詢對象
User user = jpaUserRepository.findById(id).get();
return user;
}
@RequestMapping("/findUsers")
@ResponseBody
public List<User> findUsers(String userName, String note) {
List<User> userList = jpaUserRepository.findUsers(userName, note);
return userList;
}
@RequestMapping("/getUserById")
@ResponseBody
public User getUserById(Long id) {
// 使用JPA接口查詢對象
User user = jpaUserRepository.getUserById(id);
return user;
}
@RequestMapping("/findByUserNameLike")
@ResponseBody
public List<User> findByUserNameLike(String userName) {
// 使用JPA接口查詢對象
List<User> userList = jpaUserRepository.findByUserNameLike("%" + userName + "%");
return userList;
}
@RequestMapping("/findByUserNameLikeOrNoteLike")
@ResponseBody
public List<User> findByUserNameLikeOrNoteLike(String userName, String note) {
String userNameLike = "%" + userName + "%";
String noteLike = "%" + note + "%";
// 使用JPA接口查詢對象
List<User> userList = jpaUserRepository.findByUserNameLikeOrNoteLike(userNameLike, noteLike);
return userList;
}
}
測試代碼 dao
public interface JpaUserRepository extends JpaRepository<User, Long> {
@Query("from user where user_name like concat('%', ?1, '%') "
+ "and note like concat('', ?2, '%')")
public List<User> findUsers(String userName, String note);
/**
* 按用戶名稱模糊查詢
* @param userName 用戶名
* @return 用戶列表
*/
List<User> findByUserNameLike(String userName);
/**
* 根據主鍵查詢
* @param id -- 主鍵
* @return 用戶
*/
User getUserById(Long id);
/**
* 按照用戶名稱或者備註進行模糊查詢
* @param userName 用戶名
* @param note 備註
* @return 用戶列表
*/
List<User> findByUserNameLikeOrNoteLike(String userName, String note);
}
註解掃描 和 配置
//定義JPA接口掃描包路徑
@EnableJpaRepositories(basePackages = "com.example.demobenaware.service") //默認開啓
//定義實體Bean掃描包路徑
@EntityScan(basePackages = "com.example.demobenaware.pojo") //bean掃描的路徑
//定義Spring Boot掃描包路徑
@SpringBootApplication(scanBasePackages = {"com.springboot.chapter5"})
只需要定義了 , data - jpa 會自動配置的
spring-boot-starter-data-jpa
#使用MySQL數據庫方言
spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect
#打印數據庫SQL
spring.jpa.show-sql=true
#選擇Hibernate數據定義語言(DDL)策略爲update
spring.jpa.hibernate.ddl-auto=update
命名規則
命名都是 依照 動詞 get/find開始的,by代表什麼內容進行查詢,
getUserById 通過主鍵
findByUserNameLike 多了一個like,代表模糊查詢
findByUserNameLike Or NoteLike 設計兩個條件
整合 MyBatis框架
Hibernate 的 模型有助於 系統的 分析 和 建模,
重點在於 業務模型 的 分析 和 設計
互聯網開發的難度,主要集中在 大數據 和 性能 問題
簡介
支持定製化SQL,存儲過程,以及高級映射的優秀 的持久層框架。
可以對 配置 和 原生 Map 使用簡單的 XML 或 註解,
將 接口 和 Java 的 Pojo 映射成 數據庫中的 記錄。
基於 一種 SQL 到 Pojo 的 模型。
需要我們提供 SQL 映射關係 和 POJO
提供自動映射 和 駝峯映射 等。
沒有屏蔽SQL,我們可以儘可能地 通過 SQL 去優化性能。
MyBatis的配置文件包括,基礎配置文件,映射文件。
spring 3 發佈時 mybatis 3 還沒發佈。
MyBatis 社區爲了整合 Spring 開發了相應的開發包。
- pom依賴,很重要
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency>//只引入這一個即可
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.5</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>${pagehelper.version}</version>
</dependency>
配置
基於 SqlSessionFactory構建的框架。 用這個工廠生成 SqlSession。
只存在一個 SqlSessionFactor (單例的),
Configuration 可以配置的有:
-
properties 屬性
-
settings 設置:底層行爲。自動映射 和 駝峯映射,執行器類型,緩存
-
typeAliase 類別別名:類 全限定名會比較長,所以會提供默認的別名。自定義別名
-
typeHandlers 類別處理器:java是javaType,數據庫是jdbcType。使用集中在枚舉類型上
-
objectFactory對象工程:MyBatis生成pojo調用的工廠類,無需配置
-
pulugins 插件:攔截器。最強大最危險的插件。動態代理和責任鏈完成。修改底層實現
-
databaseIdProvider 數據庫廠商標識:配置多類型數據庫支持
-
environments 數據庫環境:數據庫連接內容和事務,交給spring託管
-
mappers 映射器:MyBatis最核心的組件,提供SQL和POJO映射。
在用戶類使用別名和類別處理器
@Alias(value = "user")// MyBatis指定別名
public class User {
private Long id = null;
private String userName = null;
private String note = null;
//枚舉使用 typeHandler進行轉換
private SexEnum sex = null;
}
public enum SexEnum {
MALE(1, "男"),
FEMALE(2, "女");
private int id ;
private String name;
SexEnum(int id, String name) {
this.id = id;
this.name= name;
}
public static SexEnum getEnumById(int id) {
for (SexEnum sex : SexEnum.values()) {
if (sex.getId() == id) {
return sex;
}
}
return null;
}
}
// 聲明JdbcType爲整形
@MappedJdbcTypes(JdbcType.INTEGER)
// 聲明JavaType爲SexEnum
@MappedTypes(value=SexEnum.class)
public class SexTypeHandler extends BaseTypeHandler<SexEnum> {//繼承此抽象類
//(要求實現TypeHandler<T>接口)
// 通過列名讀取性別
@Override
public SexEnum getNullableResult(ResultSet rs, String col)
throws SQLException {
//得到col的 int 類型
int sex = rs.getInt(col);
//如果不是1 ,2 就返回 null
if (sex != 1 && sex != 2) {
return null;
}
//否則 就返回,這個查詢到的枚舉
return SexEnum.getEnumById(sex);
}
// 通過下標讀取性別
@Override
public SexEnum getNullableResult(ResultSet rs, int idx)
throws SQLException {
int sex = rs.getInt(idx);
if (sex != 1 && sex != 2) {
return null;
}
return SexEnum.getEnumById(sex);
}
// 通過存儲過程讀取性別
@Override
public SexEnum getNullableResult(CallableStatement cs, int idx)
throws SQLException {
int sex = cs.getInt(idx);
if (sex != 1 && sex != 2) {
return null;
}
return SexEnum.getEnumById(sex);
}
// 設置非空性別參數
@Override
public void setNonNullParameter(PreparedStatement ps, int idx,
SexEnum sex, JdbcType jdbcType) throws SQLException {
//把索引 和 枚舉的 ID,放入預執行 對象裏面
ps.setInt(idx, sex.getId());
}
}
使用:提供映射文件,接口,配置
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.springboot.chapter5.dao.MyBatisUserDao">
<select id="getUser" parameterType="long" resultType="user">
select id, user_name as userName, sex, note from t_user where id = #{id}
</select>
</mapper>
-
namespace 指定一個接口 。重要
-
select 代表一個查詢語句
-
id 指代這條SQL
-
parameterType 長整型
-
resultType 返回值。user ,已經在 pojo指定了,所以可以這樣使用。或者com.*.pojo.User
-
user_name as userName 列名 和 pojo保持一致 (或者 啓用 駝峯映射)
@Repository
public interface MyBatisUserDao {
public User getUser(Long id);
}
#MyBatis映射文件通配
mybatis.mapper-locations=classpath:com/springboot/chapter5/mapper/*.xml
#MyBatis掃描別名包,和註解@Alias聯用
mybatis.type-aliases-package=com.springboot.chapter5.pojo
#配置typeHandler的掃描包
mybatis.type-handlers-package=com.springboot.chapter5.typehandler
#日誌配置
#logging.level.root=DEBUG
#logging.level.org.springframework=DEBUG
#logging.level.org.org.mybatis=DEBUG
#MyBatis映射文件通配,注意這個配置只能放在 classpath目錄下
mybatis.mapper-locations=classpath:mapper/*.xml
@Controller
@RequestMapping("/mybatis")
public class MyBatisController {
@Autowired
private MyBatisUserDao myBatisUserService = null;
@RequestMapping("/getUser")
@ResponseBody
public User getUser(Long id) {
return myBatisUserService.getUser(id);
}
}
spring boot整合 myBatis
- 應該擦除 SqlSession接口的使用,而直接獲取 Mapper接口。
- Mapper是一個接口,不可以new,
- 使用 Mapper Factory Bean (對接口配置) 和 Mapper Scanner Configurer(掃描裝配接口到ioc容器)
- 還提供了 @MapperScan(建議使用)
-
使用MapperFactoryBean裝配 接口。成功
@Autowired SqlSessionFactory sqlSessionFactory;// = null @Bean public MapperFactoryBean<MyBatisUserDao> initMyBatisUserDao() { MapperFactoryBean<MyBatisUserDao> bean = new MapperFactoryBean<>(); bean.setMapperInterface(MyBatisUserDao.class); bean.setSqlSessionFactory(sqlSessionFactory); return bean; }
-
Mapper Scanner Configurer 來配置掃描。成功
@Bean public MapperScannerConfigurer mapperScannerConfig() { //定義掃描器實例 MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer(); //加載SqlSessionFactory,Spring Boot會自動生產,SqlSessionFactory實例,無需我們敢於 mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactory"); //定義掃描的包 mapperScannerConfigurer.setBasePackage("com.example.demobenaware.*"); //限定被標註@Repository的接口才被掃描 (防止誤會掃描) mapperScannerConfigurer.setAnnotationClass(Repository.class); //通過繼承某個接口限制掃描,一般使用不多 //mapperScannerConfigurer.setMarkerInterface(。。。。。。); return mapperScannerConfigurer; }
-
使用@MapperScan定義掃描
//定義Spring Boot掃描包路徑 @SpringBootApplication(scanBasePackages = {"com.springboot.chapter5"}) //定義JPA接口掃描包路徑 //@EnableJpaRepositories(basePackages = "com.springboot.chapter5.dao") //定義實體Bean掃描包路徑 //@EntityScan(basePackages = "com.springboot.chapter5.pojo") @MapperScan( //指定掃描包 basePackages = "com.springboot.chapter5.*", //指定SqlSessionFactory,如果sqlSessionTemplate被指定,則作廢 sqlSessionFactoryRef = "sqlSessionFactory", //指定sqlSessionTemplate,將忽略sqlSessionFactory的配置 sqlSessionTemplateRef = "sqlSessionTemplate", //markerInterface = Class.class,//限定掃描接口,不常用 annotationClass = Repository.class ) public class Chapter5Application { }
- 如果沒有多個sqlSessionFactory和 sqlSessionTemplate 完全可不配置
- 系統 會選擇sqlSessionTemplate (捨棄sqlSessionFactory)
- annotationClass 儘量配置,Repository 。使用@Mapper,就配置 Mapper
-
mybatis 其他配置
#MyBatis映射文件通配
mybatis.mapper-locations=classpath:mapper/*.xml
#MyBatis掃描別名包,和註解@Alias聯用
mybatis.type-aliases-package=com.example.demobenaware.pojo
#配置typeHandler的掃描包
mybatis.type-handlers-package=com.example.demobenaware.typehandler
#配置文件
mybatis.config-location= ....
#插件 攔截器
mybatis.configuration.interceptors=
#級聯延遲加在屬性配置
mybatis.configuration.aggressive-lazy-loading=false
#執行器,有simple(默認),reuse,batch
mybatis.executor-type=simple
-
配置 插件
@Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class, Integer.class }) }) public class MyPlugin implements Interceptor { Properties properties = null; // 攔截方法邏輯 @Override public Object intercept(Invocation invocation) throws Throwable { System.out.println("插件攔截方法......"); return invocation.proceed(); } // 生成MyBatis攔截器代理對象 @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } // 設置插件屬性 @Override public void setProperties(Properties properties) { this.properties = properties; } }
#配置文件 mybatis.config-location=classpath:mybatis/mybatis-config.xml <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <configuration> <plugins> <plugin interceptor="com.springboot.chapter5.plugin.MyPlugin"> <property name="key1" value="value1" /> <property name="key2" value="value2" /> </plugin> </plugins> </configuration>
@Autowired //或者使用java配置 SqlSessionFactory sqlSessionFactory = null; @PostConstruct public void initMyBatis(){ Interceptor plugin=new MyPlugin(); Properties p=new Properties(); p.setProperty("key1","value1"); plugin.setProperties(p); sqlSessionFactory.getConfiguration().addInterceptor(plugin); }