SSH笔记-Hibernate的HQL查询、QBC、本地SQL查询

1、Hibernate 提供了以下几种检索对象的方式
(1)导航对象图检索方式:根据已经加载的对象导航到其他对象
(2)OID 检索方式:按照对象的 OID 来检索对象
(3)HQL 检索方式:使用面向对象的 HQL 查询语言
(4)QBC 检索方式:使用 QBC(Query By Criteria) API 来检索对象. 这种 API 封装了基于字符串形式的查询语句, 提供了更加面向对象的查询接口
(5)本地 SQL 检索方式:使用本地数据库的 SQL 查询语句

2、如果在 HQL 中没有显式指定检索策略, 将使用映射文件配置的检索策略

3、HQL 会忽略映射文件中设置的迫切左外连接检索策略, 如果希望 HQL 采用迫切左外连接策略, 就必须在 HQL 查询语句中显式的指定它如:String hql = "SELECT DISTINCT e FROM HQLone e LEFT JOIN FETCH e.hqLtwo";

4、若在 HQL 代码中显式指定了检索策略, 就会覆盖映射文件中配置的检索策略,如果像上一点的hql语句那样,显示指定了hql检索策略,运行是会优先使用hql语句的检索策略,而不是使用配置文件中的检索策略

下面代码是以多对多关系映射

5、HQLone.java

package com.demo.sshtest;

import java.util.HashSet;
import java.util.Set;

public class HQLone {

    public Integer id;
    public String firstNameOne;
    public String lastNameOne;
    public int number;

    public Set hqLtwo = new HashSet<>();

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getFirstNameOne() {
        return firstNameOne;
    }

    public void setFirstNameOne(String firstNameOne) {
        this.firstNameOne = firstNameOne;
    }

    public String getLastNameOne() {
        return lastNameOne;
    }

    public void setLastNameOne(String lastNameOne) {
        this.lastNameOne = lastNameOne;
    }

    public Set getHqLtwo() {
        return hqLtwo;
    }

    public void setHqLtwo(Set hqLtwo) {
        this.hqLtwo = hqLtwo;
    }

    @Override
    public String toString() {
        return "HQLone [id=" + id + "]";
    }
    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }

    public HQLone(){}
    public HQLone(Integer id, String firstNameOne, int number) {
        super();
        this.id = id;
        this.firstNameOne = firstNameOne;
        this.number = number;
    }


}

6、HQLtwo.java

package com.demo.sshtest;

import java.util.HashSet;
import java.util.Set;

public class HQLtwo {

    public Integer id;
    public String firstNameTwo;
    public String lastNameTwo;

    public Set hqLone = new HashSet<>();

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getFirstNameTwo() {
        return firstNameTwo;
    }

    public void setFirstNameTwo(String firstNameTwo) {
        this.firstNameTwo = firstNameTwo;
    }

    public String getLastNameTwo() {
        return lastNameTwo;
    }

    public void setLastNameTwo(String lastNameTwo) {
        this.lastNameTwo = lastNameTwo;
    }

    public Set getHqLone() {
        return hqLone;
    }

    public void setHqLone(Set hqLone) {
        this.hqLone = hqLone;
    }
}

7、HQLone.hbm.xml




<hibernate-mapping>
    <class name="com.demo.sshtest.HQLone" table="HQLONE">
        <id name="id" type="java.lang.Integer">
            <column name="ONE_ID" />
            <generator class="native" />
        id>
        <property name="firstNameOne" type="java.lang.String">
            <column name="FIRSTNAMEONE" />
        property>
        <property name="lastNameOne" type="java.lang.String">
            <column name="LASTNAMEONE" />
        property>
        <property name="number" type="java.lang.Integer">
            <column name="NUMBER" />
        property>
        <set name="hqLtwo" table="HQLONE_HQLTWO" inverse="false" lazy="true">
            <key>
                <column name="ONE_ID" />
            key>
            <many-to-many class="com.demo.sshtest.HQLtwo" column="TWO_ID"/>
        set>
    class>
    <query name="idHQLone"> :id AND e.firstNameOne LIKE :fname]]> query>
hibernate-mapping>

这里的query标签是后面查询操作中命名查询使用的

8、HQLtwo.hbm.xml




<hibernate-mapping>
    <class name="com.demo.sshtest.HQLtwo" table="HQLTWO">
        <id name="id" type="java.lang.Integer">
            <column name="TWO_ID" />
            <generator class="native" />
        id>
        <property name="firstNameTwo" type="java.lang.String">
            <column name="FIRSTNAMETWO" />
        property>
        <property name="lastNameTwo" type="java.lang.String">
            <column name="LASTNAMETWO" />
        property>
        <set name="hqLone" table="HQLONE_HQLTWO" inverse="true" lazy="true">
            <key>
                <column name="TWO_ID" />
            key>
            <many-to-many class="com.demo.sshtest.HQLone" column="ONE_ID"/>
        set>
    class>

hibernate-mapping>

9、testHQLquery.java 测试HQL QBC和本地SQL查询的代码,其中insertOriData()是用来插入测试用的样本数据到数据库的,testQBC()/testSummaryQuery()/testSortQuery()是用来测试QBC查询的,testLocalSQL()是用来测试本地SQL查询的,testHQLupdate()是用来测试使用HQL修改和删除数据的,其他方法都是用来测试HQL查询功能

package com.demo.sshtest;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.LinkedHashSet;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.criterion.Conjunction;
import org.hibernate.criterion.Disjunction;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projection;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.id.uuid.Helper;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class testHQLquery {

    //如果在 HQL 中没有显式指定检索策略, 将使用映射文件配置的检索策略. 
    //HQL 会忽略映射文件中设置的迫切左外连接检索策略, 如果希望 HQL 采用迫切左外连接策略, 就必须在 HQL 查询语句中显式的指定它
    //若在 HQL 代码中显式指定了检索策略, 就会覆盖映射文件中配置的检索策略

    public SessionFactory sessionFactory;
    public Session session;
    public Transaction transaction;

    @Before
    public  void init(){
        Configuration configuration = new Configuration().configure();
        ServiceRegistry serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties()).buildServiceRegistry();
        sessionFactory = configuration.buildSessionFactory(serviceRegistry);
        session = sessionFactory.openSession();
        transaction = session.beginTransaction();
        System.out.println("init");
    }
    @After
    public  void destory(){
        transaction.commit();
        session.close();
        sessionFactory.close();
        System.out.println("destory");
    }
    @Test
    public void insertOriData(){
        //插入测试数据
        for(int number=1;number<10;number++){
            for(int i=0;i<2;i++){
                Date date = new Date();
                SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddHHmmssSSS");
                String time = simpleDateFormat.format(date);
                HQLone hqLone1 = new HQLone();
                HQLone hqLone2 = new HQLone();
                HQLtwo hqLtwo1 = new HQLtwo();
                HQLtwo hqLtwo2 = new HQLtwo();
                hqLone1.setFirstNameOne("1FNameOne_"+time);
                hqLone1.setLastNameOne("1LNameOne_"+time);
                hqLone1.setNumber(number);
                hqLone2.setFirstNameOne("1FNameTwo_"+time);
                hqLone2.setLastNameOne("1LNameTwo_"+time);
                hqLone2.setNumber(number);
                hqLtwo1.setFirstNameTwo("2FNameOne_"+time);
                hqLtwo1.setLastNameTwo("2LNameOne_"+time);
                hqLtwo2.setFirstNameTwo("2FNameTwo_"+time);
                hqLtwo2.setLastNameTwo("2LNameTwo_"+time);
                hqLone1.getHqLtwo().add(hqLtwo1);
                hqLone1.getHqLtwo().add(hqLtwo2);
                hqLone2.getHqLtwo().add(hqLtwo1);
                hqLone2.getHqLtwo().add(hqLtwo2);
                hqLtwo1.getHqLone().add(hqLone1);
                hqLtwo1.getHqLone().add(hqLone2);
                hqLtwo2.getHqLone().add(hqLone1);
                hqLtwo2.getHqLone().add(hqLone2);
                session.save(hqLone1);
                session.save(hqLone2);
                session.save(hqLtwo1);
                session.save(hqLtwo2);
            }
        }
    }
    @Test
    public void testHQL(){
        //基于位置参数查询
        //1、创建Query对象
        //hql里面的"HQLone"是查询对象类名,而不是数据表表名,同理,这里面的"id"、"firstNameOne"是模型类中的参数名,而不是数据库字段名
        String hql = "FROM HQLone e WHERE e.id > ? AND e.firstNameOne LIKE ? ";
        Query query = session.createQuery(hql);
        //2、绑定参数
        //setXXX
        //  ①第一个参数是hql语句中"?"的位置(即:赋值的是第几个问号)
        //  ②第二个参数是"?"对应的值
        //如果要查询实体类对象,则需要使用setEntity,而且要给类对象的id赋值
        query.setInteger(0, 10).setString(1, "%FNameOne%");
        //3、执行查询
        List rs = query.list();
        System.out.println(rs.size());
    }
    @Test
    public void testHQL2(){
        //基于命名参数查询
        //1、创建Query对象
        //hql里面的"HQLone"是查询对象类名,而不是数据表表名,同理,这里面的"id"、"firstNameOne"是模型类中的参数名,而不是数据库字段名
        String hql = "FROM HQLone e WHERE e.id > :id AND e.firstNameOne LIKE :fname ";
        Query query = session.createQuery(hql);
        //2、绑定参数
        //setXXX里面:
        //  ①第一个参数是hql语句中":"后面的参数名
        //  ②第二个参数是参数名的值
        //setEntity(): 把参数与一个持久化类绑定,使用前要给持久化类对象的id赋值
        //setParameter(): 绑定任意类型的参数. 该方法的第三个参数显式指定
        query.setInteger("id", 10).setString("fname", "%FNameOne%");
        //3、执行查询
        List rs = query.list();
        System.out.println(rs.size());
    }
    @Test
    public void testPageNavQuery(){
        //分页查询
        String hql = "FROM HQLone";
        Query query = session.createQuery(hql);
        int pageSize = 6;//一页有多少条数据
        //int pageNum = 5;//分页页码
        for(int pageNum = 1;pageNum<=5;pageNum++){
            query.setFirstResult( (pageNum-1)*pageSize ).setMaxResults(pageSize).list();
            List rs = query.list();
            System.out.println("page rs:"+rs);
        }
    }
    @Test
    public void testNamedQuery(){
        //命名查询
        //在关系映射文件里面使用query标签配置查询条件,查询条件用来包着,不然会出现字符不兼容
        //使用session的getNamedQuery()方法读取映射文件中query标签的name
        Query query = session.getNamedQuery("idHQLone");
        List hqLones = query.setInteger("id", 10).setString("fname", "%FNameOne%").list();
        for(int i = 0;i"page rs:"+hqLones.get(i));
        }
    }
    @Test
    public void testFieldQuery(){
        //投影查询
        //步骤:
        //①hql语句里面用 new 对象名(需要查询的字段) 如:new HQLone(e.id,e.firstNameOne)
        //②在对象类里面写构造器和空构造器
        //③用Query运行hql,查询出数据
        //④用for循环把query的结果循环读出来
        //⑤使用get方法获取值
        //注意事项:
        //①hql语句的new里面的字段名要跟构造函数的参数顺序和参数对得上,不然会说不能解析构造器构造
        //②如果new和构造器都没有写的的参数,获取到的只是null,不会报错
        //③Query 的 list() 方法返回的集合中包含的是数组类型的元素, 每个对象数组代表查询结果的一条记录
        //④可以在持久化类中定义一个对象的构造器来包装投影查询返回的记录
        String hql = "select new HQLone(e.id,e.firstNameOne,e.number) from HQLone e where e.id < :id";
        Query query = session.createQuery(hql);
        //因为这个时候query查出来的是对象的集合
        List rs = query.setInteger("id", 3).list();
        for(HQLone hqLone :rs){
            System.out.println(hqLone.getId()+"=="+hqLone.getFirstNameOne()+"=="+hqLone.getLastNameOne()+"=="+hqLone.getNumber());
        }
    }
    @Test
    public void testGroupQuery(){
        //报表查询
        //报表查询用于对数据分组和统计, 与 SQL 一样, HQL 利用 GROUP BY 关键字对数据分组, 用 HAVING 关键字对分组数据设定约束条件
        //可以调用的聚集函数:
        //①count()
        //②min()
        //③max()
        //④sum()
        //⑤avg()
        String hql = "select SUM(e.firstNameOne) from HQLone e where e.id< :id group by e.number";
        Query query = session.createQuery(hql);
        List hqLones = query.setInteger("id", 10).list();
        for(int i = 0;i"page rs:"+hqLones.get(i));
        }
    }

    //迫切左外连接查询、左外链接查询、迫切内连接查询、内连接查询
    //对比起来,迫切左外链接查询和迫切内连接查询比较方便
    //如果从"一"端查询的话四种查询方式都能用,如果从"多"端查询的话就应该用迫切内连接查询和内连接查询

    @Test
    public void testLeftJoinFetch(){
        //迫切左外连接查询

        //对比迫切左外连接查询和左外连接查询,推荐使用迫切左外连接查询,因为更加方便

        //注意事项:
        //①list()方法返回的集合中会存放实体对象的引用,即查询对象的关联的对象都会被初始化,并且存放到查询的对象中
        //②如果有重复数据,可以用hql或者hashset的方法去重

        //①使用方法一(通过hql去重复结果):
        //  可以用DISTINCT去掉查询结果中重复的
        //  hql中如果要差全部字段的数据 可以在表名后面定义一个代表改数据表的参数,然后sql中select后的"*"就替换成这个定义的参数
        String hql = "SELECT DISTINCT e FROM HQLone e LEFT JOIN FETCH e.hqLtwo";
        Query query = session.createQuery(hql);
        List rs = query.list();
        for(HQLone hqLone : rs){
            System.out.println(hqLone.getId()+"--"+hqLone.getFirstNameOne()+"--"+hqLone.getLastNameOne()+"--"+hqLone.getHqLtwo().size());
        }

        //②使用方法二(使用LinkedHashSet去重复结果):
        //  通过使用LinkedHashSet的key是唯一的特性去重复结果
        //  需要用ArrayList包着LinkedHashSet
        //  比较麻烦
        String hql1 = "FROM HQLone e LEFT JOIN FETCH e.hqLtwo";
        Query query1 = session.createQuery(hql1);
        List rs1 = query1.list();
        rs1 = new ArrayList<>(new LinkedHashSet(rs1));
        for(HQLone hqLone : rs){
            System.out.println(hqLone.getId()+"--"+hqLone.getFirstNameOne()+"--"+hqLone.getLastNameOne()+"--"+hqLone.getHqLtwo().size());
        }
    }
    @Test
    public void testleftJoin(){
        //左外链接查询
        //注意事项:
        //①list()方法返回的集合中不会存放实体对象的引用,即查询对象的关联的对象不会被初始化
        //②因为list()返回的集合中存放的是对象数据类型,所以,如果有重复数据,只可以用hql的方法去重
        //③根据检索对象的配置文件决定检索策略
        //④如果希望list()方法返回集合中包含关联对象,就需要在语句中使用select
        String hql = "SELECT DISTINCT e FROM HQLone e LEFT JOIN e.hqLtwo";
        Query query = session.createQuery(hql);
        List rs = query.list();
        for(HQLone hqLone : rs){
            System.out.println(hqLone.getId()+"--"+hqLone.getFirstNameOne()+"--"+hqLone.getLastNameOne()+"--"+hqLone.getHqLtwo().size());
        }
    }
    @Test
    public void testInnerJoinFetch(){
        //迫切内连接查询

        //①使用方法一(通过hql去重复结果):
        String hql = "SELECT DISTINCT e FROM HQLone e INNER JOIN FETCH e.hqLtwo";
        Query query = session.createQuery(hql);
        List rs = query.list();
        for(HQLone hqLone : rs){
            System.out.println(hqLone.getId()+"--"+hqLone.getFirstNameOne()+"--"+hqLone.getLastNameOne()+"--"+hqLone.getHqLtwo().size());
        }

        //②使用方法二(使用LinkedHashSet去重复结果):
        String hql1 = "FROM HQLone e INNER JOIN FETCH e.hqLtwo";
        Query query1 = session.createQuery(hql1);
        List rs1 = query1.list();
        rs1 = new ArrayList<>(new LinkedHashSet(rs1));
        for(HQLone hqLone : rs){
            System.out.println(hqLone.getId()+"--"+hqLone.getFirstNameOne()+"--"+hqLone.getLastNameOne()+"--"+hqLone.getHqLtwo().size());
        }
    }
    @Test
    public void testInnerJoin(){
        //内链接查询
        String hql = "SELECT DISTINCT e FROM HQLone e INNER JOIN e.hqLtwo";
        Query query = session.createQuery(hql);
        List rs = query.list();
        for(HQLone hqLone : rs){
            System.out.println(hqLone.getId()+"--"+hqLone.getFirstNameOne()+"--"+hqLone.getLastNameOne()+"--"+hqLone.getHqLtwo().size());
        }
    }
    @Test
    public void testQBC(){
        //QBC检索查询

        //1、创建Criteria对象
        Criteria criteria = session.createCriteria(HQLone.class);
        //2、添加查询条件,在QBC中查询条件用Criteria表示
        //  通过Restrictions/Order/MatchMode对象的静态方法得到Criteria

            //QBC常用限定方法
            //Restrictions.eq --> equal,等于.
            //Restrictions.allEq --> 参数为Map对象,使用key/value进行多个等于的比对,相当于多个Restrictions.eq的效果
            //Restrictions.gt --> great-than > 大于
            //Restrictions.ge --> great-equal >= 大于等于
            //Restrictions.lt --> less-than, < 小于
            //Restrictions.le --> less-equal <= 小于等于
            //Restrictions.between --> 对应SQL的between子句
            //Restrictions.like --> 对应SQL的LIKE子句,例如:like '%value%'
            //Restrictions.in --> 对应SQL的in子句
            //Restrictions.isNull --> 判断属性是否为空,为空则返回true
            //Restrictions.isNotNull --> 与isNull相反
            //Restrictions.sqlRestriction --> SQL限定的查询
            //Order.asc --> 根据传入的字段进行升序排序
            //Order.desc --> 根据传入的字段进行降序排序
            //MatchMode.EXACT --> 字符串精确匹配.相当于"like 'value'"
            //MatchMode.ANYWHERE --> 字符串在中间匹配.相当于"like '%value%'"
            //MatchMode.START --> 字符串在最前面的位置.相当于"like 'value%'"
            //MatchMode.END --> 字符串在最后面的位置.相当于"like '%value'"

            //hibernate生成的hql查询语句中,每个criteria.add()之间都用一个and关键字连接
        criteria.add(Restrictions.like("firstNameOne", "%FName%"));
        criteria.add(Restrictions.gt("id", 5));
        //and条件
        Conjunction conjunction = Restrictions.conjunction();
        conjunction.add(Restrictions.like("firstNameOne", "%Two%"));
        conjunction.add(Restrictions.le("number", 10));
        //or条件
        Disjunction disjunction = Restrictions.disjunction();
        disjunction.add(Restrictions.isNotNull("firstNameOne"));
        disjunction.add(Restrictions.between("id",25,28));

        criteria.add(conjunction);
        criteria.add(disjunction);

        //3、查询
        List rs = criteria.list();
        for(HQLone hqLone : rs) {
              System.out.println(hqLone.getId()+"--"+hqLone.getFirstNameOne()+"--"+hqLone.getLastNameOne()+"--"+hqLone.getNumber());
        }
    }
    @Test
    public void testSummaryQuery(){
        //统计查询
        //可以由Projections静态方法得到结果
        Criteria criteria = session.createCriteria(HQLone.class);
        criteria.setProjection(Projections.max("number"));
        System.out.println(criteria.uniqueResult());
    }
    @Test
    public void testSortQuery(){
        //排序查询(排序后获得分页数据)
        Criteria criteria = session.createCriteria(HQLone.class);
        //1、添加排序
        criteria.addOrder(Order.asc("number"));
        //2、添加分页方法
        int pageSize = 2;//一页有多少条数据
        int pageNo = 5;//页码
        List rs = criteria.setFirstResult((pageNo-1)*pageSize ).setMaxResults(pageSize).list();
        for(HQLone hqLone : rs) {
              System.out.println(hqLone.getId()+"--"+hqLone.getFirstNameOne()+"--"+hqLone.getLastNameOne()+"--"+hqLone.getNumber());
        }
    }
    @Test
    public void testLocalSQL(){
        //本地SQL检索,用于完成HQL或者QBC无法完成的数据库操作,如:插入数据
        String sql = "INSERT INTO HQLone values(?,?,?,?)";
        Query query = session.createSQLQuery(sql);
        query.setInteger(0, 29).setString(1, "ffffff").setString(2, "lllllll").setInteger(3, 10).executeUpdate();
    }
    @Test
    public void testHQLupdate(){
        //HQL操作数据库, 更新删除(HQL或者QBC做不了插入操作)

        //1、用本地sql插入一条数据,用于测试
        testLocalSQL();
        //2、用hql做查询操作
        String hql = "SELECT e FROM HQLone e WHERE e.id=?";
        Query query = session.createQuery(hql).setInteger(0, 29);
        List rs = query.list();
        for(HQLone hqLone : rs){
            System.out.println(hqLone.getId()+"--"+hqLone.getFirstNameOne()+"--"+hqLone.getLastNameOne()+"--"+hqLone.getHqLtwo().size());
        }
        //3、用hql做更新操作
        String hql1 = "UPDATE HQLone e SET e.firstNameOne=? , e.lastNameOne=? , e.number=? WHERE e.id=?";
        session.createQuery(hql1).setString(0, "eeee").setString(1, "wwww").setInteger(2, 11).setInteger(3, 29).executeUpdate();
        //4、用hql做删除操作
        String hql2 = "DELETE FROM HQLone e WHERE e.id=:id";
        session.createQuery(hql2).setInteger("id", 29).executeUpdate();
    }
}

其中要注意的是:①HQL或者QBC做不了插入数据的操作 ②QBC 查询就是通过使用 Hibernate 提供的 Query By Criteria API 来查询对象 ③本地SQL查询是用来完善HQL不能涵盖所有的查询特性

10、hibernate.cfg.xml



<hibernate-configuration>
    <session-factory>

        
        <property name="connection.username">rootproperty>
        <property name="connection.password">property>
        <property name="connection.driver_class">com.mysql.jdbc.Driverproperty>
        
        <property name="connection.url">jdbc:mysql://localhost/hebernateTESTproperty>

        
        <property name="dialect">org.hibernate.dialect.MySQLDialectproperty>

        
        <property name="show_sql">trueproperty>
        
        <property name="format_sql">trueproperty>

        
        <property name="hbm2ddl.auto">updateproperty>

        
        <property name="connection.isolation">2property>

        
        <property name="hibernate.use_identifier_rollback">trueproperty>

        
        <property name="hibernate.c3p0.max_size">10property>
        <property name="hibernate.c3p0.min_size">5property>
        <property name="hibernate.c3p0.timeout">2000property>
        <property name="hibernate.c3p0.max_statements">10property>
        <property name="hibernate.c3p0.idle_test_period">2000property>
        <property name="hibernate.c3p0.acquire_increment">2property>

        
        <property name="hibernate.jdbc.fetch_size">100property>
        <property name="hibernate.jdbc.batch_size">50property>

        
        <mapping resource="com/demo/sshtest/HQLone.hbm.xml"/>
        <mapping resource="com/demo/sshtest/HQLtwo.hbm.xml"/>

    session-factory>
hibernate-configuration>

11、项目目录
SSH笔记-Hibernate的HQL查询、QBC、本地SQL查询_第1张图片

12、demo
https://download.csdn.net/download/qq_22778717/10372538

你可能感兴趣的:(java)