zoukankan      html  css  js  c++  java
  • Spring框架学习(四)SpringData

    说是SpringData,其实其中包含了太多内容,同样开始看的一头雾水,其实现在还是有很多不了解的地方。

    官方文档还是讲的不错的,一开始看会比较迷茫,但是稍微看一些以后,有些疑问在里面有说明。这是地址

    这里都是基于SpringBoot的自动配置进行的,所以大部分配置比较简单。

    spring jdbc

    jdbc是java原有的数据访问组件,创建连接、创建查询、执行查询、结果通过ResultSet逐个读取转换。

    当前面加上spring名头后,指的是spring的jdbc框架(或者叫模块?)。通过spring-boot-starter-data-jdbc依赖,使spring程序中可以得到一个注入得jdbctemplate对象,而不再依赖DriverMaanger,而且简化了访问方法。当然底层还是用的jdbc

    当然jdbc并非ORM,因为还是面向与sql执行结果得,结果也需要手工转换为实体类(或者通过RowMapper)。

    配置

    配置比较少,基本使用只要配置DataSource就好了。

    其实DataSource并非jdbc的概念,它代表一个数据库的配置,后面SpringJPA也要用到。

    同样有代码和文件的配置方式,这是使用文件的方式进行配置的:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    	<bean name="dataSource"
    		class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    		<property name="url"
    			value="jdbc:mysql://localhost:3306/testdb?serverTimezone=Asia/Shanghai&amp;characterEncoding=utf8" />
    		<property name="username" value="root" />
    		<property name="password" value="123456" />
    	</bean>
    </beans>
    

    对于这个配置文件多说两句:

    1. 网上教程需要配置driver这个property,比如指向mysql,但是我用的时候,会有日志说不用配,会自动加载,我试了的确如此
    2. 注意连接字符串里的serverTimezone=Asia/Shanghai这个配置,可能是jdbc版本原因,如果不加这个,数据库连接会报时区不对的错误。

    注意在springboot入口,需要添加对这个配置文件的依赖:

    @SpringBootApplication
    @ImportResource("classpath:jdbc-config.xml")
    public class App {
    	public static void main(String[] args) {
    		new SpringApplicationBuilder(App.class).run(args);
    	}
    }
    

    也可以使用代码的方式,两者相同:

        @Bean
        public DataSource getDataSource() {
            DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
            dataSourceBuilder.url("jdbc:mysql://localhost:3306/testdb?serverTimezone=Asia/Shanghai");
            dataSourceBuilder.username("root");
            dataSourceBuilder.password("123456");
            return dataSourceBuilder.build();
        }
    

    配置就是这样,很简单

    使用jdbcTemplate

    repository需要通过注解标记,主要为了

    • 被识别为bean
    • 特殊处理其中抛出的数据库异常
    @Repository
    public class EmployeeRepository {
    	@Autowired
    	NamedParameterJdbcTemplate jdbc;
    
    	public void createEmployee(Employee employee) {
    		jdbc.execute("insert into employees (name,email) values (:name,:email)",
    				new BeanPropertySqlParameterSource(employee), (pc) -> {
    					pc.execute();
    					return null;
    				});
    	}
    
    	public void updateEmployee(long id, Employee employee) {
    		jdbc.update("update employees set name=:name, email=:email where id=:id", new MapSqlParameterSource()
    				.addValue("id", id).addValue("name", employee.getName()).addValue("email", employee.getEmail()));
    	}
    
    	public Employee getEmployee(long id) {
    		return jdbc.queryForObject("select * from employees where id=:id", new MapSqlParameterSource("id", id),
    				new EmployeeRowMapeer());
    	}
    }
    
    class EmployeeRowMapeer implements RowMapper<Employee> {
    	@Override
    	public Employee mapRow(ResultSet rs, int rownumber) throws SQLException {
    		Employee e = new Employee();
    		e.setId(rs.getInt(1));
    		e.setName(rs.getString(2));
    		e.setEmail(rs.getString(3));
    		return e;
    	}
    }
    

    这里

    1. 注册了NamedParameterJdbcTemplate,相比普通的JdbcTemplate,可以支持MapSqlParameterSourceBeanPropertySqlParameterSource两个类作为结构化参数传入
    2. 使用了update/execute等方法执行数据库操作
    3. 定义了RowMapper来转换返回结果到实体类

    spring jpa

    前言

    有一种说法是spring jpa下面使用了jdbc,不过我没有找到相关的资料。

    在spring jpa之前,同样的也有一个JPA,它时操作EnitityManager来进行ORM操作,同样有类似JDBC的复杂性,而SpringJPA封装了这个类到Repository中,使用方便了。

    不过在了解SpringJPA前,JPA也有一些概念,比如JPA有一个需要通过EntityManagerFactory创建EntityManager,而EntityManager内部由各种实现(比如hibernate)维护一个Persistance Context,里面跟踪了所有的Entity情况。

    spring jpa定义了一套接口规范,具体有一些实现组件,比如spring默认的似乎是hibernate。

    SpringJPA在使用上以Repository为基础,有下面这些内容(部分)。

    SpringJPA使用spring-boot-starter-data-jpa作为开始依赖。

    实体类

    • 通过@Entity定义实体类,所以JPA是一种ORM框架,可以将实体与数据库表关联
    @Entity
    @Table(name = "employees")
    public class Employee {
    	@Id
    	@GeneratedValue(strategy = GenerationType.AUTO)
    	long id;
    	String name;
    	String email;
      //省略getter setter
    }
    

    Repository类

    定义了Repository接口,接口有两个模板参数,一个是实体类,一个是实体类的id类型,通过这个信息,找到Repository使用的表。

    public interface Repository<T, ID> {
    }
    

    直接定义接口名称,Repository可以自动生成实现,比如定义List<Employee> findByName(String name);,就不用写方法实现,JPA内部会自动生成查询方法,并将参数传入

    public interface EmployeeRepository extends Repository<Employee, Long> {
    
    	List<Employee> findByName(String name);
    }
    

    方法里传入的SortPageable参数也能被正确转换为所需的排序或者分页信息。

    预定义的Repository

    提供了类似CrudRepository接口,定义了常用的findAll,count,findById等方法;提供了PagingAndSortingRepository,进一步增加了携带SortPageable的相关方法。

    应用的Repository集成这些类,常用的CRUD可以不用定义直接使用。

    public interface EmployeeRepository extends PagingAndSortingRepository<Employee, Long> {}
    
    

    使用时可以直接使用save方法:

    
    	@Autowired
    	EmployeeRepository repo;
    
    	@Override
    	public void createEmployee(Employee employee) {
    		repo.save(employee);
    
    	}
    

    自定义方法实现:

    当通过名称实现不满足要求时,支持JPQLSQL自定义方法。

    JPQL

    JPQL是JPA定义的SQL,会在Repository初始化的时候就进行预处理,如果其中有错误,这时就会报错。

    这是通过JPQL查询的方法:

    	@Query("select e from #{#entityName} e where name like %?1%")
    	List<Employee> CustomQuery(String name, Pageable page);
    
    • 参数可以通过位置符号?1替代,这里被替换为了name
    • #{#entityName}被自动替代为实体名字,这样不用写死在代码中
    • Pageable参数可以被SpringJPA自动识别并加入到最后的查询中
    • 注意这是一个Like语句,百分号周围没有加单引号,如果加了就得不到想要的结果了,推测应该是ORM框架自动加的
    原生sql

    同样可以通过原生SQL:

    	@Query(value = "SELECT * FROM #{#entityName} WHERE name like %:name%", nativeQuery = true)
    	List<Employee> NativeQuery(@Param("name") String name);
    

    这里通过nativeQuery标识标明sql被数据库直接执行。
    在此之前,同样可以进行参数替换,这里使用了命名参数的方式@Param("name") 对应到sql中的:name

    实体名称替换( #{#entityName})同样有效。

    同样也需要注意

    EntityManager

    可以回到java JPA中EntityManager中的方法,对比可以看出来SpringJPA帮忙省略的细节。

    注意这里扩展的方法需要实现,所以额外定义一个Cusom接口和Impl类

    public interface EmployeeRepository extends PagingAndSortingRepository<Employee, Long>, EmployeeRepositoryCustom {
    
    //	@Query(value = "SELECT * FROM #{#entityName} WHERE name like %:name%", nativeQuery = true)
    //	List<Employee> NativeQuery(@Param("name") String name);
    }
    
    interface EmployeeRepositoryCustom {
    
    	public List<Employee> NativeQuery(String name);
    }
    
    class EmployeeRepositoryImpl implements EmployeeRepositoryCustom {
    
    	@PersistenceContext
    	EntityManager entityManager;
    
    	public List<Employee> NativeQuery(String name) {
    		javax.persistence.Query query = entityManager.createNativeQuery("select * from employees where name like ?1",
    				Employee.class);
    		query.setParameter(1, name + "%");
    		List<Employee> list = query.getResultList();
    		return list;
    	}
    }
    

    相比较之前的Native SQL方式,这里同样是创建原生SQL,只是代码要复杂许多,因为暴露出来的接口EmployeeRepository还需要扩展自自定义的接口EmployeeRepositoryCustom

  • 相关阅读:
    spring boot使用自定义注解+AOP实现对Controller层指定方法的日志记录
    spring事务管理中,注解方式和xml配置方式优先级谁高?
    synchronized修饰类中不同方法,调用的时候方法互斥吗
    java(spring boot)实现二维码生成(可以插入中间log和底部文字)
    java借助Robot给微信好友自动发消息(可发送表情包)
    js中Map类型的使用
    【转】Intellij笔记
    Tomcat6.0webappsevopWEB-INFclasses (系统找不到指定的路径)
    多线程多进程之其他
    文件操作
  • 原文地址:https://www.cnblogs.com/mosakashaka/p/12609127.html
Copyright © 2011-2022 走看看