Java框架之ORM
写在前面
本文看下Java操作数据库相关的内容。
1:JDBC
我们知道关系型数据库不止一种,像MySQL,Oracle,db2,PostgreSQL,sql server等,为了规范对这些不同数据的连接,数据的CRUD操作, Java官方便设计了一套接口,如DriverManager,Connection,Statement,ResultSet,DriverManager等,用来规范这些操作,这个规范就是JDBC在,rt.jar包的中的java.sql包下,如下图:

JDBC和应用程序,以及各种数据库之间的关系如下图:

那么最终如何连接到数据库并操作数据库呢,这就需要不同的数据库厂商按照JDBC的规范来提供一个具体的实现,这个具体的实现我们叫做是数据库驱动,如MySQL的驱动包mysql-connector-java-xxx.jar,oracle的驱动包ojdbc-xxx.jar,接下来我们也分别以MySQL和Oracle为例子看下如何操作数据库。
1.1:MySQL
源码 。
首先引入pom:
<dependencies><dependency>
<groupId>mysqlgroupId><artifactId>mysql-connector-javaartifactId><version>5.1.47version>dependency>
dependencies>
接着写测试程序大家自己创建表a0707,然后创建两个字段录数据就行:
public class MySqlMain {public static void main(String[] args) throws Exception {//1、导入驱动jar包//2、注册驱动Class.forName("com.mysql.jdbc.Driver");//3、获取数据库的连接对象Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3366/test", "root", "root");//4、定义sql语句String sql = "SELECT * FROM a0707 ";//5、获取执行sql语句的对象Statement stat = con.createStatement();//6、执行获取结果ResultSet rs = stat.executeQuery(sql);while(rs.next()){String one = rs.getString(1);String two = rs.getString(2);System.out.println("one is: " + one + ", two is: " + two);}}
}
运行:
one is: 1, two is: xxx
one is: 2, two is: d7df5f01-fdc7-11ec-b06f-00ff2dadabf5
one is: 3, two is: d7df7a50-fdc7-11ec-b06f-00ff2dadabf5
one is: 4, two is: d7df7ad2-fdc7-11ec-b06f-00ff2dadabf5
...
这样我们就通过MySQL的JDBC驱动程序成功操作MySQL数据库了。
1.2:Oracle
在这里 下载驱动包。
首先配置驱动包:
<dependency><groupId>oraclegroupId><artifactId>oracleartifactId><version>5version><scope>systemscope><systemPath>${project.basedir}/jar/ojdbc-10g.jarsystemPath>
dependency>
这里的systemPath根据本地环境自行修改,当然如果有线上资源的话,直接用也行。
接着写测试程序大家自己创建表AREA,然后创建两个字段录数据就行:
public class OracleMain {public static void main(String[] args) throws Exception {//1、导入驱动jar包//2、注册驱动Class.forName("oracle.jdbc.driver.OracleDriver");//3、获取数据库的连接对象Connection con = DriverManager.getConnection("jdbc:oracle:thin:@192.168.10.251:1521/orcl", "jc6_jcs", "jinher.2023");//4、定义sql语句String sql = "SELECT * FROM AREA ";//5、获取执行sql语句的对象Statement stat = con.createStatement();//6、执行获取结果ResultSet rs = stat.executeQuery(sql);while(rs.next()){String one = rs.getString(1);String two = rs.getString(2);System.out.println("one is: " + one + ", two is: " + two);}}
}
运行:
one is: 337, two is: 繁峙县
one is: 338, two is: 宁武县
one is: 339, two is: 静乐县
...
这样我们就通过oracle的JDBC驱动程序成功操作oracle数据库了。
可以看到,虽然操作了完全不同的数据库,但是我们只需要引入对应的驱动包,然后修改驱动类,以及数据库地址信息就行了,核心的数据操作逻辑完全不用动,这就是抽象的好处,规范的好处。
2:数据库连接池
我们知道线程资源比较珍贵,而且创建的成本很高,所以我们就有了连接池技术 ,数据库连接也同样如此,所以,我们也同样需要数据库连接的一种池化的技术,也就是数据库连接池,数据库连接池构建于JDBC之上,通过JDBC获取一组Connection,并按照一定的策略对其进行维护,如最大连接数,最小连接数,空闲多久回收等,如下图:

主要的数据库连接池实现如下:
C3P0:目前用到不多
DBCP:apatch common pool
Druid:阿里大牛开发,提供了sql审计功能,目前有一定市场
hikari:海卡里,日语“光”的意思,目前性能最好的数据库连接池,用的比较多
3:orm
源码 。
orm全称是object relation mapping,即对象关系映射,描述的是Java中的对象和数据库表的一种映射关系。数据库的表和Java的对象是差一层的,通过Java的对象不能直接映射到表,因此这就需要做一个转换,而这个转换的工作也势必是需要有一种规范的,因此SUN公司就是提出了JPA,Java persistence API,是由一组接口和抽象类组成的Java类到数据库表映射的一个规范,如下图:

实现了JPA规范的orm框架事实上的标准就是hibernate,其他的框架还有toplink,openJpa等,应用程序使用JPA规范,底层是具体实现了JPA规范的orm框架,如下图:

实际上,各种orm框架底层和数据库的交互还是通过JDBC完成的,因此可以认为JPA是在JDBC基础上以面向对象操作数据库的方式进行了进一步的封装,如下图:

除了hibernate,我们常用的orm框架还有mybatis,但是不同于hibernate,mybatis并没有遵循JPA规范,而是直接基于JDBC开发的,允许开发人员以直接写sql语句,并通过resultmap的方式映射到对象,接下来我们分别看下hibernate和mybatis的用法。
3.1:hibernate
首先我们创建表:
CREATE TABLE `user` (`id` int(11) NOT NULL,`user_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,PRIMARY KEY (`id`),UNIQUE KEY `uk_account` (`user_name`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
接着引入依赖:
<dependencies><dependency><groupId>junitgroupId><artifactId>junitartifactId><version>${junit.version}version><scope>testscope>dependency><dependency><groupId>javax.servletgroupId><artifactId>javax.servlet-apiartifactId><version>${servlet.version}version><scope>providedscope>dependency><dependency><groupId>mysqlgroupId><artifactId>mysql-connector-javaartifactId><version>${mysql.version}version>dependency><dependency><groupId>javax.servletgroupId><artifactId>jstlartifactId><version>${jstl.version}version>dependency><dependency><groupId>taglibsgroupId><artifactId>standardartifactId><version>${taglibs.version}version>dependency><dependency><groupId>org.apache.tomcatgroupId><artifactId>tomcat-jsp-apiartifactId><version>${tomcat.version}version>dependency><dependency><groupId>org.hibernategroupId><artifactId>hibernate-coreartifactId><version>${hibernate.version}version>dependency><dependency><groupId>org.projectlombokgroupId><artifactId>lombokartifactId><version>1.18.12version>dependency>
dependencies>
创建映射表的Java对象:
@Getter
@Setter
@AllArgsConstructor
public class User implements Serializable{private Integer id;private String userName;
}
创建映射java对象和表的配置文件User.hbm.xml:
DOCTYPE hibernate-mapping PUBLIC"-//Hibernate/Hibernate Mapping DTD 3.0//EN""http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping><class name="dongshi.daddy.User" table="user"><id name="id" type="java.lang.Integer" column="id"><generator class="increment">generator>id><property name="userName" type="java.lang.String" column="user_name"/>class>
hibernate-mapping>
配置hibernate的配置文件hibernate.cfg.xml:
DOCTYPE hibernate-configuration PUBLIC"-//Hibernate/Hibernate Configuration DTD 3.0//EN""http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration><session-factory><property name="connection.username">rootproperty><property name="connection.password">rootproperty><property name="connection.url">jdbc:mysql://localhost:3366/test?useUnicode=true&characterEncoding=UTF-8&userSSL=falseproperty><property name="connection.driver_class">com.mysql.jdbc.Driverproperty><property name="dialect">org.hibernate.dialect.MySQLDialectproperty><property name="hibernate.current_session_context_class">threadproperty><property name="show_sql">trueproperty><property name="format_sql">trueproperty><mapping resource="hbm/User.hbm.xml"/>session-factory>
hibernate-configuration>
测试类:
public class HibernateJpaMain {public static void main(String[] args) {//创建Hibernate核心类Configuration cfg=new Configuration();//读取核心配置文件cfg.configure("hibernate.cfg.xml");//创建session工厂SessionFactory sf = cfg.buildSessionFactory();//获取sessionSession session = sf.openSession();//开启事务Transaction ts= session.beginTransaction();System.out.println("-------------增加-------------------");//新增User user=new User(111, "阿三同学");session.save(user);ts.commit();}
}
运行:
INFO: HHH000182: No default (no-argument) constructor for class: dongshi.daddy.User (class must be instantiated by Interceptor)
-------------增加-------------------
Hibernate: selectmax(id) fromuser
Hibernate: insert intouser(user_name, id) values(?, ?)
查看表:

3.2:mybatis
首先我们创建表:
CREATE TABLE test_mybatis (id INT(32) PRIMARY KEY AUTO_INCREMENT,full_name VARCHAR(64) DEFAULT NULL,age INT(32)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
创建实体类:
@Getter
@Setter
@AllArgsConstructor
public class TestMybatis {private Integer id;private String fullName;private Integer age;
}
创建mapper接口:
public interface TestMyBatisMapper {void insertTestMybatis(TestMybatis testMybatis);
}
创建mapper xml my-mapper.xml:
DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="dongshi.daddy.mapper.TestMyBatisMapper"><insert id="insertTestMybatis" parameterType="dongshi.daddy.TestMybatis">INSERT INTO test_mybatis (full_name,age)VALUES(#{fullName},#{age});insert>
mapper>
上述的namespace和parameterType注意改成自己的。
创建全局配置文件mybatis-config.xml:
DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><settings><setting name="mapUnderscoreToCamelCase" value="true"/>settings><environments default="test"><environment id="test"><transactionManager type="JDBC" /><dataSource type="POOLED"><property name="driver" value="com.mysql.jdbc.Driver" /><property name="url" value="jdbc:mysql://127.0.0.1:3366/test" /><property name="username" value="root" /><property name="password" value="root" />dataSource>environment>environments><mappers><mapper resource="dongshi/daddy/mapper/my-mapper.xml"/>mappers>
configuration>
测试代码:
public class TestMyBatisMapperTest {private TestMyBatisMapper testMyBatisMapper;private SqlSession sqlSession;@Beforepublic void setUp() throws Exception {String resource = "mybatis-config.xml";InputStream inputStream = Resources.getResourceAsStream(resource);SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);SqlSession sqlSession = sqlSessionFactory.openSession();this.sqlSession = sqlSession;this.testMyBatisMapper = sqlSession.getMapper(TestMyBatisMapper.class);}@Testpublic void insertTestMyBatis() {TestMybatis newTestMyBatis = new TestMybatis(1212, "别吵吵,只有中国人解放军才能保卫台湾", 33);
// newTestMyBatis.setFullName(UUID.randomUUID().toString());
// newTestMyBatis.setAge(new Random().nextInt(100));testMyBatisMapper.insertTestMybatis(newTestMyBatis);this.sqlSession.commit();}
}
运行后:

截止到这里我们看下JDBC,JPA,ORM框架之间的关系:

另外,spring针对常见数据存储组件提供了spring-data 项目 ,如下图:

我们来看下spring-data-jpa是如何操作数据库的
3.3:spring data jpa
spring data jpa 进一步进一步封装了hibernate,其和JDBC,ORM等关系如下图:

spring data jpa支持如下三种查询方式:
1:通过方法名查询:将查询语句写到接口的名称中;
2:通过 Example 对象查询:构造一个模板对象,使用 findAll 方法来查询;
3:自定义查询:通过 Query 注解自定义复杂查询语句。
基于方法名称:

基于Example:

自定义查询:

下面我们详细看下。
- 创建表
CREATE TABLE `jpa_user` (`id` bigint(64) NOT NULL AUTO_INCREMENT,`name` varchar(63) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4
- xml
<dependencyManagement><dependencies><dependency><groupId>org.springframework.bootgroupId><artifactId>spring-boot-dependenciesartifactId><version>2.2.10.RELEASEversion><type>pomtype><scope>importscope>dependency>dependencies>
dependencyManagement><dependencies><dependency><groupId>org.springframework.bootgroupId><artifactId>spring-boot-starter-webartifactId>dependency><dependency><groupId>org.springframework.bootgroupId><artifactId>spring-boot-starter-data-jpaartifactId>dependency><dependency><groupId>com.alibabagroupId><artifactId>druid-spring-boot-starterartifactId><version>1.1.23version>dependency>
<dependency><groupId>mysqlgroupId><artifactId>mysql-connector-javaartifactId><version>5.1.47version>dependency><dependency><groupId>org.projectlombokgroupId><artifactId>lombokartifactId><optional>trueoptional>dependency>dependencies>
- 表对应实体
@Data
@Entity
@Table(name = "jpa_user")
//@EntityListeners(AuditingEntityListener.class)
public class JpaUser {@Id@Column(name = "id")@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "JPA_USER_S")@SequenceGenerator(sequenceName = "JPA_USER_S", name = "JPA_USER_S", allocationSize = 1)private Long id;@Column(name = "name")private String name;
}
- yml配置
spring:datasource:type: com.alibaba.druid.pool.DruidDataSourceurl: jdbc:mysql://localhost:3366/testusername: rootpassword: rootdriver-class-name: com.mysql.jdbc.Driverjpa:hibernate:ddl-auto: update #自动更新show-sql: true #日志中显示sql语句application:name: spring-data-jpa-demo
server:port: 2333 #端口号
- repository service controller
public interface JpaUserRepository extends JpaRepository<JpaUser, Long> {
}
public interface JpaUserService {/*** 新增用户* @param user 用户对象*/JpaUser insertUser(JpaUser user);
}
@Service
public class JpaUserServiceImpl implements JpaUserService {@Resourceprivate JpaUserRepository jpaUserRepository;@Overridepublic JpaUser insertUser(JpaUser user) {return jpaUserRepository.save(user);}
}
@RestController
@RequestMapping("/user")
public class JpaUserController {@Resourceprivate JpaUserService jpaUserService;@PostMapping("/addUser")public JpaUser addUser(@RequestBody JpaUser user){return jpaUserService.insertUser(user);}}
- main
/*** 启动类*/
//@EnableJpaAuditing
@SpringBootApplication
public class SpringContextApplication {public static void main(String[] args) {SpringApplication.run(SpringContextApplication.class, args);}
}
- 启动访问

console输出:
2023-07-05 16:52:57.115 INFO 14556 --- [nio-2333-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 4 ms
Hibernate: select jpauser0_.id as id1_0_0_, jpauser0_.name as name2_0_0_ from jpa_user jpauser0_ where jpauser0_.id=?
Hibernate: select next_val as id_val from jpa_user_s for update
Hibernate: update jpa_user_s set next_val= ? where next_val=?
Hibernate: insert into jpa_user (name, id) values (?, ?)

4:其他
4.1:spring jdbc
封装了JDBC,提供了简单的数据库操作,我们经常用到的JdbcTemplate就在此功能内,如下图:

看下其如何使用。
- 表
CREATE TABLE `user` (`id` int(11) NOT NULL,`user_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,PRIMARY KEY (`id`),UNIQUE KEY `uk_account` (`user_name`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
- pom
<dependencyManagement><dependencies><dependency><groupId>org.springframework.bootgroupId><artifactId>spring-boot-dependenciesartifactId><version>2.2.10.RELEASEversion><type>pomtype><scope>importscope>dependency>dependencies>
dependencyManagement><dependencies><dependency><groupId>org.springframework.bootgroupId><artifactId>spring-boot-starter-jdbcartifactId>dependency><dependency><groupId>mysqlgroupId><artifactId>mysql-connector-javaartifactId><version>5.1.47version>dependency><dependency><groupId>org.projectlombokgroupId><artifactId>lombokartifactId><optional>trueoptional>dependency>dependencies>
- dao
@Repository
public class UserDao {@Resourceprivate JdbcTemplate jdbcTemplate;public void addUser() {String insertSql = "INSERT INTO `user`(id, `user_name`) VALUES (?, ?) ";jdbcTemplate.update(insertSql, new Object[]{ 999, "精确治理是个啥?" });}
}
- main
注意实现了ApplicationRunner接口,我们在其run方法中直接插入数据了。
@SpringBootApplication
public class SpringJdbcApplication implements ApplicationRunner {public static void main(String[] args) {SpringApplication.run(SpringJdbcApplication.class, args);}@Resourceprivate UserDao userDao;@Overridepublic void run(ApplicationArguments args) throws Exception {userDao.addUser();}
}
运行:

4.2:事务
针对事务管理,spring定义了接口PlatformTransactionManager,针对不同的操作数据库的方式提供了不同的事务管理类,如对JDBC提供了DataSourceTransactionManager,Hibernate提供了HibernateTransactionManager,是通过AOP的方式来实现的。
写在后面
参考文章列表
JDBC连接Mysql数据库详解 。
JDBC连接Mysql数据库详解 。
学习笔记之JPA连接数据库 。
JPA、Hibernate、Spring data jpa 之间的关系,终于明白了 。
Java获取类、方法、属性上的注解 。
mybatis学习文章系列(偏源码) 。
最详细的Spring-data-jpa入门(一) 。
Spring Boot(三): 操作数据库-Spring JDBC 。
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
